# frozen_string_literal: true

require "English"
require "csv"

module Decidim
  module Votings
    module Census
      module Admin
        # A command with the business logic to create census dataset for a
        # voting space.
        class CreateDataset < Decidim::Command
          include Decidim::ProcessesFileLocally

          def initialize(form, current_user)
            @form = form
            @current_user = current_user
            @dataset = nil
          end

          # Executes the command. Broadcast this events:
          # - :ok when everything is valid
          # - :invalid when the form was not valid and could not proceed-
          #
          # Returns nothing.
          def call
            return broadcast(:invalid) unless form.valid?

            process_file_locally(form.file) do |file_path|
              @file_path = file_path
              dataset = create_census_dataset!

              if csv_header_invalid?
                dataset.destroy!
                return broadcast(:invalid_csv_header)
              end

              if dataset
                CSV.foreach(file_path, col_sep: Decidim.default_csv_col_sep, headers: true, converters: ->(f) { f&.strip }) do |row|
                  CreateDatumJob.perform_later(current_user, dataset, row.fields)
                end
              end
            end

            broadcast(:ok)
          end

          private

          attr_reader :form, :current_user, :file_path
          attr_accessor :dataset

          def create_census_dataset!
            Decidim.traceability.create(
              Decidim::Votings::Census::Dataset,
              current_user,
              {
                voting: form.current_participatory_space,
                filename: form.file.filename.to_s,
                csv_row_raw_count: csv_row_count,
                status: :creating_data
              },
              visibility: "admin-only"
            )
          end

          def csv_header_invalid?
            headers.blank? || headers != expected_headers
          end

          def headers
            @headers ||= CSV.parse_line(File.open(file_path), col_sep: ";", headers: true, header_converters: :symbol)&.headers
          end

          def no_ballot_headers
            [:document_id, :document_type, :date_of_birth, :full_name, :full_address, :postal_code, :mobile_phone_number, :email_address]
          end

          def ballot_style_headers
            no_ballot_headers.push(:ballot_style_code)
          end

          def expected_headers
            @expected_headers ||= form.current_participatory_space.has_ballot_styles? ? ballot_style_headers : no_ballot_headers
          end

          def csv_row_count
            @csv_row_count ||= file_lines_count - 1
          end

          # count lines in the most resource-efficient way using ruby, handles milions of lines with minimal memory footprint
          def file_lines_count
            lines = 0
            File.foreach(file_path) { lines += 1 }
            lines
          end
        end
      end
    end
  end
end