class ProcedureExportService
  include DossierHelper

  ATTRIBUTES = [
    :id,
    :created_at,
    :updated_at,
    :archived,
    :email,
    :state,
    :initiated_at,
    :received_at,
    :processed_at,
    :motivation,
    :emails_instructeurs,
    :individual_gender,
    :individual_prenom,
    :individual_nom,
    :individual_birthdate
  ]

  ETABLISSEMENT_ATTRIBUTES = [
    :siret,
    :siege_social,
    :naf,
    :libelle_naf,
    :adresse,
    :numero_voie,
    :type_voie,
    :nom_voie,
    :complement_adresse,
    :code_postal,
    :localite,
    :code_insee_localite
  ]

  ENTREPRISE_ATTRIBUTES = [
    :siren,
    :capital_social,
    :numero_tva_intracommunautaire,
    :forme_juridique,
    :forme_juridique_code,
    :nom_commercial,
    :raison_sociale,
    :siret_siege_social,
    :code_effectif_entreprise,
    :date_creation,
    :nom,
    :prenom
  ]

  def initialize(procedure, tables: [], ids: nil, since: nil, limit: nil)
    @procedure = procedure
    @dossiers = procedure.dossiers.downloadable_sorted
    if ids
      @dossiers = @dossiers.where(id: ids)
    end
    if since
      @dossiers = @dossiers.since(since)
    end
    if limit
      @dossiers = @dossiers.limit(limit)
    end
    @dossiers = @dossiers.to_a
    @tables = tables.map(&:to_sym)
  end

  def to_csv
    SpreadsheetArchitect.to_csv(to_data(:dossiers))
  end

  def to_xlsx
    package = SpreadsheetArchitect.to_axlsx_package(to_data(:dossiers))

    # Next we recursively build multi page spreadsheet
    @tables.reduce(package) do |package, table|
      SpreadsheetArchitect.to_axlsx_package(to_data(table), package)
    end.to_stream.read
  end

  def to_ods
    spreadsheet = SpreadsheetArchitect.to_rodf_spreadsheet(to_data(:dossiers))

    # Next we recursively build multi page spreadsheet
    @tables.reduce(spreadsheet) do |spreadsheet, table|
      SpreadsheetArchitect.to_rodf_spreadsheet(to_data(table), spreadsheet)
    end.bytes
  end

  def to_data(table)
    case table
    when :dossiers
      dossiers_table_data
    when :etablissements
      etablissements_table_data
    end
  end

  private

  def empty_table_data(sheet_name, headers = [])
    {
      sheet_name: sheet_name,
      headers: headers,
      data: [[]]
    }
  end

  def dossiers_table_data
    if @dossiers.any?
      {
        sheet_name: 'Dossiers',
        headers: dossiers_headers,
        data: dossiers_data
      }
    else
      empty_table_data('Dossiers', dossiers_headers)
    end
  end

  def etablissements_table_data
    @etablissements = @dossiers.flat_map do |dossier|
      [dossier.champs, dossier.champs_private]
        .flatten
        .select { |champ| champ.is_a?(Champs::SiretChamp) }
    end.map(&:etablissement).compact

    if @etablissements.any?
      {
        sheet_name: 'Etablissements',
        headers: etablissements_headers,
        data: etablissements_data
      }
    else
      empty_table_data('Etablissements', etablissements_headers)
    end
  end

  def dossiers_headers
    headers = ATTRIBUTES.map(&:to_s) +
      @procedure.types_de_champ.reject(&:exclude_from_export?).map(&:libelle) +
      @procedure.types_de_champ_private.reject(&:exclude_from_export?).map(&:libelle) +
      ETABLISSEMENT_ATTRIBUTES.map { |key| "etablissement.#{key}" } +
      ENTREPRISE_ATTRIBUTES.map { |key| "entreprise.#{key}" }

    headers.map { |header| label_for_export(header) }
  end

  def dossiers_data
    @dossiers.map do |dossier|
      values = ATTRIBUTES.map do |key|
        case key
        when :email
          dossier.user.email
        when :state
          dossier_legacy_state(dossier)
        when :initiated_at
          dossier.en_construction_at
        when :received_at
          dossier.en_instruction_at
        when :individual_prenom
          dossier.individual&.prenom
        when :individual_nom
          dossier.individual&.nom
        when :individual_birthdate
          dossier.individual&.birthdate
        when :individual_gender
          dossier.individual&.gender
        when :emails_instructeurs
          dossier.followers_gestionnaires.map(&:email).join(' ')
        else
          dossier.read_attribute(key)
        end
      end

      normalize_values(values) +
        dossier.champs.reject(&:exclude_from_export?).map(&:for_export) +
        dossier.champs_private.reject(&:exclude_from_export?).map(&:for_export) +
        etablissement_data(dossier.etablissement)
    end
  end

  def etablissements_headers
    headers = ["dossier_id", "libelle"] +
      ETABLISSEMENT_ATTRIBUTES.map { |key| "etablissement.#{key}" } +
      ENTREPRISE_ATTRIBUTES.map { |key| "entreprise.#{key}" }

    headers.map { |header| label_for_export(header) }
  end

  def etablissements_data
    @etablissements.map do |etablissement|
      [
        etablissement.champ.dossier_id,
        label_for_export(etablissement.champ.libelle).to_s
      ] + etablissement_data(etablissement)
    end
  end

  def etablissement_data(etablissement)
    data = ETABLISSEMENT_ATTRIBUTES.map do |key|
      if etablissement.present?
        case key
        when :adresse
          etablissement.adresse&.chomp&.gsub("\r\n", ' ')&.delete("\r")
        else
          etablissement.read_attribute(key)
        end
      end
    end
    data += ENTREPRISE_ATTRIBUTES.map do |key|
      if etablissement.present?
        case key
        when :date_creation
          etablissement.entreprise_date_creation&.to_datetime
        else
          etablissement.read_attribute(:"entreprise_#{key}")
        end
      end
    end
    normalize_values(data)
  end

  def label_for_export(label)
    label.parameterize.underscore.to_sym
  end

  def normalize_values(values)
    values.map do |value|
      case value
      when TrueClass, FalseClass
        value.to_s
      else
        value.blank? ? nil : value.to_s
      end
    end
  end
end
