# typed: true

require 'cli/kit'

module CLI
  module Kit
    # INI is a language similar to JSON or YAML, but simplied
    # The spec is here: https://en.wikipedia.org/wiki/INI_file
    # This parser includes supports for 2 very basic uses
    # - Sections
    # - Key Value Pairs (within and outside of the sections)
    #
    # [global]
    # key = val
    #
    # Nothing else is supported right now
    # See the ini_test.rb file for more examples
    #
    class Ini
      extend T::Sig

      sig { returns(T::Hash[String, T::Hash[String, String]]) }
      attr_accessor :ini

      sig do
        params(path: T.nilable(String), config: T.nilable(String), default_section: String).void
      end
      def initialize(path = nil, config: nil, default_section: '[global]')
        @config = if path && File.exist?(path)
          File.readlines(path)
        elsif config
          config.lines
        end
        @ini = {}
        @current_key = default_section
      end

      sig { returns(T::Hash[String, T::Hash[String, String]]) }
      def parse
        return @ini if @config.nil?

        @config.each do |l|
          l.strip!

          if section_designator?(l)
            @current_key = l
          else
            k, v = l.split('=', 2).map(&:strip)
            set_val(k, v) if k && v
          end
        end

        @ini
      end

      sig { returns(String) }
      def git_format
        to_ini(git_format: true)
      end

      sig { returns(String) }
      def to_s
        to_ini
      end

      private

      sig { params(git_format: T::Boolean).returns(String) }
      def to_ini(git_format: false)
        optional_tab = git_format ? "\t" : ''
        str = []
        @ini.each do |section_designator, section|
          str << '' unless str.empty? || git_format
          str << section_designator
          section.each do |k, v|
            str << "#{optional_tab}#{k} = #{v}"
          end
        end
        str.join("\n")
      end

      sig { params(key: String, val: String).void }
      def set_val(key, val)
        current_key = @current_key
        @ini[current_key] ||= {}
        @ini[current_key][key] = val
      end

      sig { params(k: String).returns(T::Boolean) }
      def section_designator?(k)
        k.start_with?('[') && k.end_with?(']')
      end
    end
  end
end