# app/services/romaneios/enriquecimento_gade.rb
#
# Preenche APARELHO, TELEFONES e NOME do romaneio a partir da tabela da operação
# (gade_entregas_*). É o que dispensa a SEGUNDA PLANILHA do programa Python: lá o
# operador subia um .xlsx só com NOTA FISCAL + STATUS para marcar quem é NOVO; aqui
# o STATUS já está no banco, na própria operação.
#
# A tabela é SOMENTE LEITURA (apenas SELECT), o nome passa por Operacao.valida? +
# quote_table_name e as NFs por conn.quote.
module Romaneios
  class EnriquecimentoGade
    # chave usada no SELECT => coluna real na gade.
    #
    # ⚠️ `status` PRECISA sair aliasado: o espelho de rastreio também tem uma coluna
    # `status` (o estado da visita), e qualquer query que junte as duas trocaria uma
    # pela outra em silêncio. Analytics::OperacaoMetricas já faz esse mesmo alias
    # (GADE_OPCIONAIS) pelo mesmo motivo.
    COLUNAS = {
      'nota_fiscal'   => 'nota_fiscal',
      'nome_completo' => 'nome_completo',
      'telefones'     => 'telefones',
      'status'        => 'status_gade'
    }.freeze

    # Sem essas, não há o que enriquecer.
    ESSENCIAIS = %w[nota_fiscal].freeze

    attr_reader :tabela

    def initialize(tabela)
      @tabela = tabela.to_s
      raise ArgumentError, "Operação inválida: #{@tabela}" unless Operacao.valida?(@tabela)
    end

    # { '90507' => { aparelho: 'SIM', telefones: '(11) ...', nome: 'CICERA ...' } }
    # NF que não estiver na operação simplesmente não vem no Hash — é assim que a
    # tela marca "fora da operação" (plano avulso / de inclusão), mesma semântica de
    # Analytics::NotasForaOperacao.
    def para_notas(notas)
      lista = Array(notas).map { |n| n.to_s.strip }.reject(&:empty?).uniq
      return {} if lista.empty?

      registros(lista).each_with_object({}) do |r, memo|
        nf = r['nota_fiscal'].to_s.strip
        next if nf.empty?

        memo[nf] = {
          aparelho:  Normalizador.aparelho_por_status(r['status_gade']),
          telefones: r['telefones'].to_s.strip,
          nome:      Normalizador.nome(r['nome_completo'])
        }
      end
    end

    # Colunas que esta operação NÃO tem — a tela precisa DIZER isso.
    #
    # O esquema das gade_entregas_* varia por operação (só `nota_fiscal` é
    # garantida). Quando falta `status`, o APARELHO sai vazio para TODO MUNDO — e um
    # romaneio em que ninguém recebe aparelho, entregue calado, é pior do que um
    # aviso feio na tela.
    def colunas_ausentes
      @colunas_ausentes ||= COLUNAS.keys - existentes
    end

    def utilizavel?
      (ESSENCIAIS - existentes).empty?
    end

    def label
      Operacao.label(@tabela)
    end

    private

    def conn
      ActiveRecord::Base.connection
    end

    def existentes
      @existentes ||= conn.columns(@tabela).map(&:name)
    end

    def registros(lista)
      return [] unless utilizavel?

      valores = lista.map { |n| conn.quote(n) }.join(', ')
      sql = "SELECT #{selects} FROM #{conn.quote_table_name(@tabela)} g " \
            "WHERE g.nota_fiscal IN (#{valores})"
      conn.select_all(sql).to_a
    end

    # Coluna que não existir vira NULL com o mesmo alias, preservando a forma do
    # resultado — mesmo padrão de SimpliRoute::PlanilhaCarga#selects. As chaves de
    # COLUNAS são whitelist fixa (não há entrada de usuário no SQL).
    def selects
      COLUNAS.map do |coluna, apelido|
        existentes.include?(coluna) ? "g.#{coluna} AS #{apelido}" : "CAST(NULL AS text) AS #{apelido}"
      end.join(', ')
    end
  end
end
