module Aigu
  class IosImporter
    DICT_DICT_OPEN_REGEX = /^\s*<dict>\s*$/
    DICT_DICT_CLOSE_REGEX = /^\s*<\/dict>\s*$/
    DICT_KEY_REGEX = /^\s*<key>(?<text>.*)<\/key>\s*$/
    DICT_STRING_REGEX = /^(?<left>\s*<string>)(?<text>.*)(?<right><\/string>\s*)$/

    def initialize(opts = {})
      @input_file = opts[:'input-file']
      @output_directory = opts[:'output-directory']
      @locale = opts[:locale]
    end

    def process!
      puts "Generating IOS strings files in `#{@output_directory}` based on Accent-generated `#{@input_file}` file"
      puts '---'

      parse_json
      strings, dict = split_dict
      write_strings_files(strings)
      write_stringsdict_file(dict)

      puts '---'
      puts 'Done'
    end

  protected

    def parse_json
      json = File.read(@input_file)
      @object = JSON.parse(json)
    end

    def split_dict
      strings = {}
      dict = {}

      @object.each_pair do |key, value|
        match_data = key.match(/^__@DICT__.*/)

        if match_data
          dict[key] = value
        else
          strings[key] = value
        end
      end

      [strings, dict]
    end

    def write_strings_files(content)
      @locale || @locale = 'en'

      file_path = File.join(@output_directory, "#{@locale}.lproj", 'Localizable.strings')
      puts "Generating #{file_path}"
      FileUtils.mkdir_p(File.dirname(file_path))

      File.open(file_path, 'w+:bom|utf-8') do |file|
        file << format_strings_file(content)
      end
    end

    def format_strings_file(content)
      file_content = ''

      content.each_pair do |key, value|
        file_content << '"' << key << '" = "' << replace_string_interpolations(value) << '";' << "\n"
      end

      file_content
    end

    # This takes a string and replaces iOS interpolations to Android interpolations
    # so that it is reusable across platforms.
    # Example: 'iOS interpolation %s or %1$s' => 'Android interpolation %@ or %1$@'.
    def replace_string_interpolations(string)
      string.gsub(/%([0-9]+\$)?s/, '%\\1@')
    end

    def write_stringsdict_file(content)
      # uses same logic as exporter to parse current file & update in memory

      # read file
      file_path = File.join(@output_directory, "#{@locale}.lproj", 'Localizable.stringsdict')
      return unless File.exist? file_path

      puts "Updating #{file_path}"

      file_content = File.open(file_path, 'rb:bom|utf-8').read

      # in memory update
      file_content = update_stringsdict_content(file_content, content)

      # write back
      File.open(file_path, 'w+:bom|utf-8') do |file|
        file << file_content
      end
    end

    # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/BlockNesting
    def update_stringsdict_content(file_content, new_hash)
      # Custom parsing in order to keep the file as-is, with any comments and spaces that dev used

      new_content = ''
      in_root_dict = false
      bundle_key = nil
      string_key = nil
      in_string_dict = false
      plural_key = nil

      file_content.each_line do |line|
        if line.match(DICT_DICT_OPEN_REGEX)
          # <dict>
          if !in_root_dict
            in_root_dict = true
          elsif string_key
            in_string_dict = true
          end

          # dict open lines, keep as is
          new_content << line
        elsif line.match(DICT_DICT_CLOSE_REGEX)
          # </dict>
          if in_string_dict
            in_string_dict = false
            string_key = nil
            plural_key = nil
          elsif bundle_key
            bundle_key = nil
          else
            in_root_dict = false
          end

          # dict close lines, keep as is
          new_content << line
        elsif matched_data = line.match(DICT_KEY_REGEX)
          # <key>..</key>
          if in_root_dict
            if bundle_key
              if string_key
                plural_key = matched_data[:text]
              else
                string_key = matched_data[:text]
              end
            else
              bundle_key = matched_data[:text]
            end
          end

          # key lines, keep as is
          new_content << line
        elsif matched_data = line.match(DICT_STRING_REGEX)
          # <string>..</string>
          if string_key && !in_string_dict
            string_key = nil

            # useless string lines, keep as is
            new_content << line
          elsif in_string_dict && plural_key
            if %w(zero one other).include? plural_key
              hash_key = "__@DICT__#{bundle_key}__@STRING__#{string_key}__@#{plural_key.upcase}"

              # replace value, keeps other parts of the line as is
              new_content << matched_data[:left] << new_hash[hash_key].encode(xml: :text) << matched_data[:right]
            else
              # useless string lines, keep as is
              new_content << line
            end
          else
            # useless string lines, keep as is
            new_content << line
          end
        else
          # unknown line, keep as is
          new_content << line
        end
      end

      new_content
    end
    # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/BlockNesting
  end
end