# app/services/analytics/totais_por_motorista.rb
#
# Quanto cada motorista somou no período — e QUAIS consolidações formam esse
# total. É a fonte única das duas telas de "total por motorista":
#
#   • admin    → /consolidacoes?visao=motoristas (modo de visualização da própria
#                lista, com drill-down no motorista clicado)
#   • motorista→ /motorista (o painel, já recortado nele — mesma conta, mesmo PDF)
#
# RECORTE — É O MESMO DO DASHBOARD FINANCEIRO, de propósito
# (DashboardController#carregar_dados_financeiro). Os dois números são lidos lado
# a lado pelo operador; quando divergem, a leitura é "o sistema está errado".
# Entram as consolidações ATIVAS que:
#   • CRUZAM o período (data_inicio <= fim AND data_fim >= inicio) — um
#     fechamento de 01/08 a 31/08 aparece ao filtrar 01/08–26/08; OU
#   • tiveram algum PAGAMENTO dentro do período (pagar em agosto um fechamento
#     de julho tem que aparecer em agosto).
#
# STATUS: por padrão só as FINALIZADAS — é o "Consolidado" do dashboard, o valor
# que já fechou. 'todos' inclui os rascunhos em andamento (a visão "Geral"), que
# ainda mudam de valor.
#
# Só motoristas ATIVOS (não arquivados) contam: é o mesmo conjunto que a
# consolidação paga (ConsolidacaoMotorista.ativos), então o total desta tela
# bate com a soma dos fechamentos.
module Analytics
  class TotaisPorMotorista
    # status:    'finalizada' (padrão — só o que já fechou), 'rascunho' ou
    #            'todos'. É o MESMO select de status da lista de consolidações.
    # motorista: recorte exato (drill-down / painel do motorista).
    # busca:     filtro parcial por nome, o campo de busca da lista.
    def initialize(inicio:, fim:, motorista: nil, status: 'finalizada', busca: nil)
      @inicio    = inicio.to_date
      @fim       = fim.to_date
      @motorista = motorista.presence
      @status    = status.presence || 'finalizada'
      @busca     = busca.presence
    end

    attr_reader :inicio, :fim, :motorista, :status

    # ConsolidacaoMotorista do recorte, com a consolidação já carregada (as duas
    # telas mostram nome/período/status dela em seguida).
    def registros
      @registros ||= begin
        base = Consolidacao.ativas
        # `public_send` sobre o enum: quem traduz :finalizada/:rascunho é o model.
        base = base.public_send(@status) if %w[finalizada rascunho].include?(@status)

        escopo = ConsolidacaoMotorista.ativos
                                      .where(consolidacao_id: ids_do_periodo(base))
                                      .includes(:consolidacao)
        escopo = escopo.where(motorista_nome: @motorista) if @motorista
        escopo = escopo.where('motorista_nome ILIKE ?', "%#{@busca}%") if @busca
        escopo.to_a
      end
    end

    # Uma linha por motorista, do maior total para o menor.
    def linhas
      @linhas ||= registros.group_by(&:motorista_nome).map do |nome, cms|
        pagos     = cms.select(&:pago?)
        pendentes = cms.reject(&:pago?)
        {
          motorista:     nome,
          consolidacoes: cms.size,
          lancamentos:   lancamentos_por_motorista[nome].to_i,
          valor_total:   soma(cms),
          valor_pago:    soma(pagos),
          valor_pendente: soma(pendentes),
          pagas:         pagos.size,
          pendentes:     pendentes.size
        }
      end.sort_by { |l| -l[:valor_total] }
    end

    # Consolidações que compõem o total de UM motorista (o drill-down), da mais
    # recente para a mais antiga.
    def consolidacoes_de(nome)
      registros.select { |cm| cm.motorista_nome == nome }
               .sort_by { |cm| [cm.consolidacao.data_fim, cm.consolidacao.data_inicio] }
               .reverse
    end

    def motoristas
      linhas.map { |l| l[:motorista] }
    end

    def total_geral    = soma(registros)
    def total_pago     = soma(registros.select(&:pago?))
    def total_pendente = soma(registros.reject(&:pago?))
    def total_consolidacoes = registros.map(&:consolidacao_id).uniq.size
    def vazio? = registros.empty?

    def periodo_label
      "#{@inicio.strftime('%d/%m/%Y')} a #{@fim.strftime('%d/%m/%Y')}"
    end

    private

    # As consolidações do período: as que cruzam a faixa + as que receberam
    # pagamento dentro dela. Mesmo par de conjuntos do dashboard financeiro.
    def ids_do_periodo(base)
      intervalo = @inicio.beginning_of_day..@fim.end_of_day

      cruzam = base.cruzando_periodo(@inicio, @fim).ids
      pagas  = ConsolidacaoMotorista.ativos
                                    .where(pago_em: intervalo)
                                    .where(consolidacao_id: base.select(:id))
                                    .distinct.pluck(:consolidacao_id)
      (cruzam + pagas).uniq
    end

    def soma(cms)
      cms.sum { |cm| cm.valor_total || 0 }
    end

    # Lançamentos classificados por motorista (soma da quantidade — um termo com
    # quantidade 5 vale 5). Uma query só para todas as consolidações do recorte,
    # em vez de uma por linha da tabela.
    def lancamentos_por_motorista
      @lancamentos_por_motorista ||= begin
        ids = registros.map(&:consolidacao_id).uniq
        if ids.empty?
          {}
        else
          # O EXISTS espelha o `ConsolidacaoMotorista.ativos` de `registros`, e por
          # PAR (consolidação, motorista) — filtrar só por nome não basta: o
          # mesmo motorista pode estar ativo numa consolidação do período e
          # arquivado em outra, e aí os lançamentos da arquivada entravam nesta
          # contagem enquanto o R$ da mesma linha os excluía. Arquivar motorista
          # é reversível e NÃO apaga consolidacao_entregas (só o excluir apaga).
          ConsolidacaoEntrega.where(consolidacao_id: ids,
                                    motorista_nome: registros.map(&:motorista_nome).uniq)
                             .where(<<~SQL.squish)
                               EXISTS (SELECT 1 FROM consolidacao_motoristas cm
                                        WHERE cm.consolidacao_id = consolidacao_entregas.consolidacao_id
                                          AND cm.motorista_nome  = consolidacao_entregas.motorista_nome
                                          AND cm.arquivado_em IS NULL)
                             SQL
                             .group(:motorista_nome)
                             .sum(:quantidade)
        end
      end
    end
  end
end
