# frozen_string_literal: true

require "json"

module Decidim
  module Admin
    module Import
      module Readers
        # Imports any exported JSON file to local objects. It transforms the
        # import data using the creator into the final target objects.
        class JSON < Base
          MIME_TYPE = "application/json"

          def read_rows
            json_string = File.read(file)
            columns = []
            data = ::JSON.parse(json_string)
            data.each_with_index do |row, index|
              row = flat_hash(row)
              if index.zero?
                columns = row.keys
                yield columns.map(&:to_s), index
              end

              values = columns.map { |c| row[c] }
              last_present = values.rindex { |v| !v.nil? }
              if last_present
                yield values[0..last_present], index + 1
              else
                yield [], index + 1
              end
            end
          rescue ::JSON::ParserError
            raise Decidim::Admin::Import::InvalidFileError, "The provided JSON file is not valid"
          end

          # Returns a StringIO
          def example_file(data)
            columns = data.shift
            json_data = data.map do |row|
              deep_hash(
                columns.each_with_index.to_h { |col, ind| [col, row[ind]] }
              )
            end

            ::StringIO.new(::JSON.pretty_generate(json_data))
          end

          private

          # Converts e.g. structure as follows:
          # { title: { en: => "Foo", es: => "Bar" } }
          #
          # Into:
          # { "title/en": "Foo", "title/es": "Bar" }
          def flat_hash(data)
            {}.tap do |final|
              data.each do |key, value|
                if value.is_a?(Hash)
                  flat_hash(value).each do |subkey, subvalue|
                    final[:"#{key}/#{subkey}"] = subvalue
                  end
                else
                  final[key.to_sym] = value
                end
              end
            end
          end

          # Converts e.g. structure as follows:
          # { "title/en": "Foo", "title/es": "Bar" }
          #
          # Into:
          # { title: { en: "Foo", es: "Bar" } }
          def deep_hash(data)
            {}.tap do |final|
              data.each do |key, value|
                keyparts = key.to_s.split("/")
                current = final
                while (keypart = keyparts.shift&.to_sym)
                  if keyparts.any?
                    current[keypart] ||= {}
                    current = current[keypart]
                  else
                    current[keypart] = value
                  end
                end
              end
            end
          end
        end
      end
    end
  end
end
