# frozen_string_literal: true

module Decidim
  module Admin
    module Import
      # Class providing the interface and implementation of an importer. Needs
      # a reader to be passed to the constructor which handles the import file
      # reading depending on its type.
      #
      # You can also use the ImporterFactory class to create an Importer
      # instance.
      class Importer
        include Decidim::ProcessesFileLocally

        delegate :errors, to: :verifier

        # Public: Initializes an Importer.
        #
        # file   - A file with the data to be imported.
        # reader - A Reader to be used to read the data from the file.
        # creator - A Creator class to be used during the import.
        # context - A hash including component specific data.
        def initialize(file:, reader: Readers::Base, creator: Creator, context: nil)
          @file = file
          @reader = reader
          @creator = creator
          @context = context
          @data_headers = []
        end

        def verify
          verifier.valid?
        end

        # Import data and create resources
        #
        # Returns an array of resources
        def prepare
          @prepare ||= collection.map(&:produce)
        end

        # Save resources
        def import!
          collection.map(&:finish!)
        end

        # Returns a collection of creators
        def collection
          @collection ||= collection_data.map { |item| creator.new(item, context) }
        end

        def invalid_file?
          collection.blank?
        rescue Decidim::Admin::Import::InvalidFileError
          true
        end

        private

        attr_reader :file, :reader, :creator, :context, :data_headers

        def verifier
          # Prepare needs to be called so that data headers become available.
          data = prepare
          @verifier ||= creator.verifier_klass.new(
            headers: data_headers.map(&:to_s),
            data:,
            reader:,
            context:
          )
        end

        def collection_data
          return @collection_data if @collection_data

          @collection_data = []
          process_file_locally(file) do |file_path|
            reader.new(file_path).read_rows do |rowdata, index|
              if index.zero?
                @data_headers = rowdata.map { |d| d.to_s.to_sym }
              else
                @collection_data << rowdata.each_with_index.to_h do |val, ind|
                  [@data_headers[ind], val]
                end
              end
            end
          end

          @collection_data
        end

        def component
          context[:current_component]
        end

        def available_locales
          @available_locales ||= component.participatory_space.organization.available_locales
        end
      end
    end
  end
end
