# app/controllers/consolidacao_entregas_controller.rb
class ConsolidacaoEntregasController < ApplicationController
  include Auditavel

  before_action :set_consolidacao
  before_action :bloquear_se_finalizada, except: %i[validar revisar]

  # Rótulo/sentinela do grupo de entregas sem veículo no filtro "fechar por carro".
  SEM_VEICULO = '(sem veículo)'.freeze

  # GET /consolidacoes/:consolidacao_id/consolidacao_entregas/validar?motorista=X
  # WIZARD PASSO 2 — tela de validação com toggles
  def validar
    authorize @consolidacao, :show?
    @motorista = params[:motorista]
    @veiculos_sel = veiculos_param(params[:vehicle])  # filtro por veículo(s) — multi-seleção

    # Entregas atendidas do período no banco existente (read-only): sucesso +
    # insucesso, pois o motorista foi ao local e a entrega precisa constar.
    # Período pela DATA REAL (checkout), não pela planejada.
    @entregas = Entrega.atendidas
                       .da_conta_gade
                       .no_periodo_checkout(@consolidacao.data_inicio, @consolidacao.data_fim)
                       .do_motorista(@motorista)
    @entregas = @entregas.da_veiculo(@consolidacao.vehicle_ids)  if @consolidacao.vehicle_ids.present?
    @entregas = @entregas.da_operacoes(@consolidacao.operacoes)  if @consolidacao.operacoes.present?
    @entregas = filtrar_por_veiculo(@entregas, @veiculos_sel)    if @veiculos_sel.any?
    @entregas = @entregas.order(:checkout)

    # Classificações já feitas — agrupadas por tracking_id (uma entrega pode ter
    # vários pilares). { tracking_id => [ConsolidacaoEntrega, ...] }
    @classificacoes = @consolidacao.consolidacao_entregas
                                   .where(motorista_nome: @motorista)
                                   .group_by(&:tracking_id)

    # Status "fechado" por veículo (filtro + acompanhamento) e totais com escopo:
    # quando um carro está selecionado, a barra reflete só aquele carro.
    @veiculos_status = veiculos_status(@motorista)
    if @veiculos_sel.any?
      sel = @veiculos_status.select { |v| @veiculos_sel.include?(v[:vehicle]) }
      @total_entregas = sel.sum { |v| v[:eligible] }
      @classificadas  = sel.sum { |v| v[:classificadas] }
    else
      @total_entregas = @consolidacao.entregas_elegiveis_count(@motorista)
      @classificadas  = @consolidacao.classificadas_count(@motorista)
    end

    # Nomes dos motoristas ATIVOS — alimenta o seletor do apontamento 100% manual.
    @motoristas_consolidacao = @consolidacao.consolidacao_motoristas.ativos.pluck(:motorista_nome)

    # Lançamentos manuais do motorista (apontamentos + termo) — feedback no resumo
    # e na barra de progresso. Ficam fora do conjunto elegível do período.
    @manuais = contar_manuais(@consolidacao.consolidacao_entregas
                                           .where(motorista_nome: @motorista, manual: true))

    # Filtro "apenas não classificadas"
    if params[:apenas_pendentes] == '1'
      @entregas = @entregas.reject { |e| @classificacoes.key?(e.tracking_id) }
    end

    @precos = {
      entrega_normal: Configuracao.preco_entrega,
      retirada:       Configuracao.preco_retirada,
      bonus:          Configuracao.preco_bonus,
      desconto:       Configuracao.preco_desconto,
      extraordinaria: Configuracao.preco_extraordinaria,
      termo:          Configuracao.preco_termo,
      termo_especial: Configuracao.preco_termo_especial
    }
  end

  # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/classificar
  # Classifica UMA entrega (chamado pelo toggle via Turbo/fetch)
  def classificar
    authorize @consolidacao, :update?

    return render_nao_elegivel unless entrega_elegivel?(params[:motorista], params[:tracking_id])

    # Toggle do pilar: se já existe, remove; senão, adiciona (permite combinar
    # vários pilares na mesma entrega).
    ce = @consolidacao.consolidacao_entregas
                      .find_by(tracking_id: params[:tracking_id], tipo: params[:tipo])
    if ce
      ce.destroy
      ativo = false
    else
      @consolidacao.consolidacao_entregas.create!(
        tracking_id:    params[:tracking_id],
        motorista_nome: params[:motorista],
        tipo:           params[:tipo],
        valor_aplicado: valor_classificacao(params[:tipo]),
        vehicle:        veiculo_do_tracking(params[:tracking_id]), # snapshot p/ fechar por veículo
        created_by:     current_user.id
      )
      ativo = true
    end

    recalcular_motorista(params[:motorista])

    respond_to do |format|
      format.json do
        render json: resumo_json(params[:motorista], vehicles: params[:vehicle]).merge(
          ativo: ativo, tracking_id: params[:tracking_id], tipo: params[:tipo]
        )
      end
      format.html { redirect_back fallback_location: wizard_consolidacao_path(@consolidacao) }
    end
  end

  # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/classificar_em_massa
  # Ações em massa: marcar todos / marcar selecionados
  def classificar_em_massa
    authorize @consolidacao, :update?

    tipo         = params[:tipo]
    motorista    = params[:motorista]
    tracking_ids = Array(params[:tracking_ids]).reject(&:blank?)

    return render_nao_elegivel unless motorista_na_consolidacao?(motorista)

    # Recorte por veículo(s) selecionado(s). Sem veículo = todos.
    elegiveis = tracking_ids_elegiveis(motorista, params[:vehicle])
    # Sem tracking_ids = todas as elegíveis do período/veículo (botão "todos").
    # Com tracking_ids → mantém só as que de fato são elegíveis para o motorista.
    tracking_ids = tracking_ids.empty? ? elegiveis : (tracking_ids & elegiveis)

    remover = params[:acao].to_s == 'remover'

    ActiveRecord::Base.transaction do
      if remover
        # Desmarca o pilar das entregas selecionadas (mantém os demais pilares).
        @consolidacao.consolidacao_entregas
                     .where(motorista_nome: motorista, tipo: tipo, tracking_id: tracking_ids)
                     .destroy_all
      else
        # ADICIONA o pilar às entregas (não remove os demais já marcados).
        valor    = valor_classificacao(tipo)
        veiculos = veiculos_dos_trackings(tracking_ids) # tracking_id => vehicle (snapshot)
        tracking_ids.each do |tid|
          ce = @consolidacao.consolidacao_entregas.find_or_initialize_by(tracking_id: tid, tipo: tipo)
          ce.assign_attributes(motorista_nome: motorista,
                               valor_aplicado: valor, created_by: current_user.id)
          ce.vehicle = veiculos[tid] if ce.vehicle.blank?
          ce.save!
        end
      end
    end

    recalcular_motorista(motorista)
    auditar!(:editar, @consolidacao,
             dados_novos: { acao_massa: tipo, modo: (remover ? 'remover' : 'adicionar'),
                            qtd: tracking_ids.size, motorista: motorista })

    respond_to do |format|
      format.json { render json: resumo_json(motorista, vehicles: params[:vehicle]) }
      format.html do
        verbo = remover ? 'desmarcadas de' : 'marcadas como'
        redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: motorista, vehicle: veiculos_param(params[:vehicle]).presence),
                    notice: "#{tracking_ids.size} entregas #{verbo} #{tipo.humanize}."
      end
    end
  end

  # GET /consolidacoes/:consolidacao_id/consolidacao_entregas/revisar?motorista=X
  # WIZARD PASSO 3 — revisão do motorista
  def revisar
    authorize @consolidacao, :show?
    @motorista = params[:motorista]

    todas = @consolidacao.consolidacao_entregas.where(motorista_nome: @motorista)

    # Agregados calculados sobre o conjunto COMPLETO (não só a página exibida).
    # sum(:quantidade) (default 1) conta os termos lançados em lote como N entregas.
    @resumo = todas.group(:tipo).sum(:quantidade)
    @total  = todas.sum do |c|
      c.tipo == 'desconto' ? -c.valor_aplicado : c.valor_aplicado
    end
    @total_pilares       = todas.count
    @total_classificadas = todas.distinct.count(:tracking_id)

    # Paginação só da tabela exibida — 20 padrão, com seletor 20/50/100
    @por_pagina = [20, 50, 100].include?(params[:por_pagina].to_i) ? params[:por_pagina].to_i : 20
    begin
      @pagy, @classificacoes = pagy(todas.order(:created_at), limit: @por_pagina)
    rescue Pagy::OverflowError
      @pagy, @classificacoes = pagy(todas.order(:created_at), limit: @por_pagina, page: 1)
    end

    # Próximo motorista pendente (para botão "Próximo Motorista") — só ativos
    nomes_completos = @consolidacao.consolidacao_motoristas.ativos.map(&:motorista_nome)
    @proximo = nomes_completos.find do |nome|
      next false if nome == @motorista
      total = @consolidacao.entregas_elegiveis_count(nome)
      @consolidacao.classificadas_count(nome) < total
    end
  end

  # GET /consolidacoes/:consolidacao_id/consolidacao_entregas/buscar_nf?nf=123
  # APONTAMENTO MANUAL — busca a NF na SimpleRoute (sem filtro de período).
  def buscar_nf
    authorize @consolidacao, :update?

    nf = params[:nf].to_s.strip
    return render(json: { ok: false, erro: 'Informe o número da nota.' }, status: :unprocessable_entity) if nf.blank?

    entregas = Entrega.da_conta_gade.por_nf(nf).order(:checkout)

    if entregas.empty?
      render json: { ok: false, erro: "Nota #{nf} não encontrada na base de rastreio." }
    else
      render json: { ok: true, nf: nf, entregas: entregas.map(&:resumo_apontamento) }
    end
  end

  # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/apontar
  # Cria um apontamento manual a partir de um tracking_id encontrado pela NF.
  # Aceita um ou vários pilares (tipos[]) na mesma entrega.
  def apontar
    authorize @consolidacao, :update?

    entrega = Entrega.da_conta_gade.find_by(tracking_id: params[:tracking_id])
    return render_apontamento_erro('Entrega não encontrada na base de rastreio.') unless entrega

    motorista = @consolidacao.adicionar_apontamento!(entrega, tipos_param, current_user)
    auditar!(:editar, @consolidacao,
             dados_novos: { apontamento_manual: entrega.numero_nf, tipos: tipos_param, motorista: motorista })

    respond_to do |format|
      format.json { render json: resumo_json(motorista).merge(motorista: motorista) }
      format.html do
        redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: motorista),
                    notice: "Apontamento da nota #{entrega.numero_nf} adicionado para #{motorista}."
      end
    end
  rescue ArgumentError => e
    render_apontamento_erro(e.message)
  end

  # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/apontar_manual
  # Apontamento 100% MANUAL — não consulta a base de rastreio. O admin informa o
  # motorista (pode ser outro que não o da tela), a NF (texto livre), uma
  # observação/local e os pilares. Usado quando o motorista foi ao local mas a
  # NF ficou registrada com outro motorista no rastreio.
  def apontar_manual
    authorize @consolidacao, :update?

    motorista = @consolidacao.adicionar_apontamento_manual!(
      motorista:   params[:motorista].to_s.strip,
      tipos:       tipos_param,
      nf:          params[:nf],
      obs:         params[:obs],
      valor_extra: valor_extraordinaria_param,
      vehicle:     params[:vehicle], # veículo escolhido no lançamento (Q3)
      user:        current_user
    )
    auditar!(:editar, @consolidacao,
             dados_novos: { apontamento_100_manual: params[:nf], motorista: motorista, tipos: tipos_param })

    respond_to do |format|
      format.json { render json: resumo_json(motorista).merge(motorista: motorista) }
      format.html do
        redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: motorista),
                    notice: "Apontamento manual adicionado para #{motorista}."
      end
    end
  rescue ArgumentError => e
    render_apontamento_erro(e.message)
  end

  # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/apontar_termo
  # ENTREGA DE TERMO — lança lotes de termos para o motorista. Não há NF nem
  # código de rastreio: só as quantidades (normal e especial), cada tipo a um
  # preço fixo configurável.
  def apontar_termo
    authorize @consolidacao, :update?

    qtd_normal   = params[:quantidade].to_i
    qtd_especial = params[:quantidade_especial].to_i
    raise ArgumentError, 'Informe ao menos um termo (normal ou especial).' if qtd_normal < 1 && qtd_especial < 1

    motorista = nil
    ActiveRecord::Base.transaction do
      { 'termo' => qtd_normal, 'termo_especial' => qtd_especial }.each do |tipo, qtd|
        next if qtd < 1

        motorista = @consolidacao.adicionar_termos!(
          motorista:  params[:motorista].to_s.strip,
          quantidade: qtd,
          tipo:       tipo,
          vehicle:    params[:vehicle], # veículo escolhido no lançamento (Q3)
          user:       current_user
        )
      end
    end
    auditar!(:editar, @consolidacao,
             dados_novos: { entrega_termo: qtd_normal, entrega_termo_especial: qtd_especial, motorista: motorista })

    respond_to do |format|
      format.json { render json: resumo_json(motorista).merge(motorista: motorista) }
      format.html do
        redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: motorista),
                    notice: "#{qtd_normal + qtd_especial} entrega(s) de termo adicionada(s) para #{motorista}."
      end
    end
  rescue ArgumentError => e
    render_apontamento_erro(e.message)
  end

  # DELETE /consolidacoes/:consolidacao_id/consolidacao_entregas/remover_apontamento
  # Remove um apontamento manual — por ?id= (um pilar) ou por ?tracking_id=&motorista=
  # (todos os pilares daquela nota). Não passa pelo guard de elegibilidade, pois o
  # tracking está fora do período (por isso não reusa o toggle classificar).
  def remover_apontamento
    authorize @consolidacao, :update?

    scope     = @consolidacao.consolidacao_entregas.apontamentos
    registros = if params[:id].present?
                  Array(scope.find_by(id: params[:id]))
                else
                  scope.where(tracking_id: params[:tracking_id], motorista_nome: params[:motorista]).to_a
                end
    return render_apontamento_erro('Apontamento não encontrado.') if registros.empty?

    motorista = registros.first.motorista_nome
    tracking  = registros.first.tracking_id
    registros.each(&:destroy)
    @consolidacao.recalcular_motorista!(motorista)
    auditar!(:editar, @consolidacao,
             dados_novos: { remover_apontamento_manual: tracking, motorista: motorista })

    respond_to do |format|
      format.json { render json: resumo_json(motorista).merge(motorista: motorista) }
      format.html { redirect_back fallback_location: consolidacao_path(@consolidacao), notice: 'Apontamento removido.' }
    end
  end

  # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/fechar_veiculo
  # Consolida (fecha) UM veículo do motorista. Exige que o veículo esteja 100%
  # classificado. O pagamento continua sendo por motorista (não é pago aqui).
  def fechar_veiculo
    authorize @consolidacao, :update?
    motorista = params[:motorista]
    veiculo   = params[:vehicle].to_s
    return render_veiculo_erro('Veículo/motorista inválido.') unless motorista_na_consolidacao?(motorista) && veiculo.present?

    status = veiculos_status(motorista).find { |v| v[:vehicle] == veiculo }
    return render_veiculo_erro('Classifique todas as entregas deste veículo antes de consolidar.') if status && !status[:completo]

    cv = @consolidacao.consolidacao_veiculos.find_or_create_by!(motorista_nome: motorista, vehicle: veiculo)
    cv.fechar!(current_user)
    auditar!(:editar, @consolidacao, dados_novos: { fechar_veiculo: veiculo, motorista: motorista })

    responder_veiculo(motorista, "Veículo #{veiculo} consolidado.")
  end

  # POST .../reabrir_veiculo — reabre um veículo já consolidado (reversível).
  def reabrir_veiculo
    authorize @consolidacao, :update?
    motorista = params[:motorista]
    cv = @consolidacao.consolidacao_veiculos.find_by(motorista_nome: motorista, vehicle: params[:vehicle].to_s)
    return render_veiculo_erro('Veículo não encontrado.') unless cv

    cv.reabrir!(current_user)
    auditar!(:editar, @consolidacao, dados_novos: { reabrir_veiculo: cv.vehicle, motorista: motorista })
    responder_veiculo(motorista, "Veículo #{cv.vehicle} reaberto.")
  end

  # POST .../arquivar_veiculo — tira um veículo do gate de finalização (reversível).
  # As entregas já classificadas dele continuam somando no pagamento do motorista.
  def arquivar_veiculo
    authorize @consolidacao, :update?
    motorista = params[:motorista]
    veiculo   = params[:vehicle].to_s
    return render_veiculo_erro('Veículo/motorista inválido.') unless motorista_na_consolidacao?(motorista) && veiculo.present?

    cv = @consolidacao.consolidacao_veiculos.find_or_create_by!(motorista_nome: motorista, vehicle: veiculo)
    cv.arquivar!(current_user)
    auditar!(:editar, @consolidacao, dados_novos: { arquivar_veiculo: veiculo, motorista: motorista })
    responder_veiculo(motorista, "Veículo #{veiculo} arquivado — fora da finalização.")
  end

  # POST .../desarquivar_veiculo — restaura um veículo arquivado.
  def desarquivar_veiculo
    authorize @consolidacao, :update?
    motorista = params[:motorista]
    cv = @consolidacao.consolidacao_veiculos.find_by(motorista_nome: motorista, vehicle: params[:vehicle].to_s)
    return render_veiculo_erro('Veículo não encontrado.') unless cv

    cv.desarquivar!(current_user)
    auditar!(:editar, @consolidacao, dados_novos: { desarquivar_veiculo: cv.vehicle, motorista: motorista })
    responder_veiculo(motorista, "Veículo #{cv.vehicle} restaurado.")
  end

  private

  def set_consolidacao
    @consolidacao = Consolidacao.find(params[:consolidacao_id])
  end

  # Resposta padrão das ações de veículo: JSON com o resumo atualizado (barra +
  # chips com o novo estado) ou redirect de volta pra tela de validação.
  def responder_veiculo(motorista, aviso)
    respond_to do |format|
      format.json { render json: resumo_json(motorista, vehicles: params[:vehicle_sel]) }
      format.html do
        redirect_to validar_consolidacao_consolidacao_entregas_path(
          @consolidacao, motorista: motorista, vehicle: veiculos_param(params[:vehicle_sel]).presence
        ), notice: aviso
      end
    end
  end

  def render_veiculo_erro(msg)
    respond_to do |format|
      format.json { render json: { ok: false, erro: msg }, status: :unprocessable_entity }
      format.html do
        redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: params[:motorista]),
                    alert: msg
      end
    end
  end

  # Veículo (snapshot da rastreio) de UM tracking_id. nil para sintéticos.
  def veiculo_do_tracking(tracking_id)
    Entrega.da_conta_gade.find_by(tracking_id: tracking_id)&.vehicle
  end

  # Mapa tracking_id => vehicle para um lote (uma query só).
  def veiculos_dos_trackings(ids)
    return {} if ids.blank?

    Entrega.da_conta_gade.where(tracking_id: ids).pluck(:tracking_id, :vehicle).to_h
  end

  def bloquear_se_finalizada
    return unless @consolidacao.finalizada? && !current_user.pode?('consolidacao.editar_finalizada')
    redirect_to @consolidacao, alert: 'Consolidação finalizada — apenas admin/gerente podem editar.'
  end

  # Motorista realmente faz parte desta consolidação? (ignora arquivados)
  def motorista_na_consolidacao?(nome)
    nome.present? && @consolidacao.consolidacao_motoristas.ativos.exists?(motorista_nome: nome)
  end

  # tracking_ids atendidos/elegíveis para o motorista no período (respeita veículos).
  # Atendidas = sucesso + insucesso com checkout (o motorista foi ao local).
  # `vehicle` (opcional) recorta para UM carro — é o que faz "Marcar todos" fechar
  # só o veículo selecionado.
  def tracking_ids_elegiveis(motorista, vehicle = nil)
    return [] unless motorista.present?

    rel = Entrega.atendidas.da_conta_gade
                 .no_periodo_checkout(@consolidacao.data_inicio, @consolidacao.data_fim)
                 .do_motorista(motorista)
    rel = rel.da_veiculo(@consolidacao.vehicle_ids)  if @consolidacao.vehicle_ids.present?
    rel = rel.da_operacoes(@consolidacao.operacoes)  if @consolidacao.operacoes.present?
    rel = filtrar_por_veiculo(rel, vehicle)          if vehicle.present?
    rel.pluck(:tracking_id)
  end

  # Normaliza o parâmetro de veículo (string única OU array vindo de vehicle[])
  # numa lista limpa de selecionados.
  def veiculos_param(raw)
    Array(raw).reject(&:blank?)
  end

  # Recorte por um OU MAIS veículos (trata o grupo "sem veículo"). Lista vazia
  # = sem recorte (todos os veículos).
  def filtrar_por_veiculo(rel, veiculos)
    lista = veiculos_param(veiculos)
    return rel if lista.empty?
    valores  = lista.reject { |v| v == SEM_VEICULO }
    valores += [nil, ''] if lista.include?(SEM_VEICULO)
    rel.where(vehicle: valores)
  end

  # Status "fechado" por veículo do motorista: lista de
  # { vehicle:, eligible:, classificadas:, fechado: }. Fonte única do filtro de
  # carros e do acompanhamento (chips). 2 queries: elegíveis (com a coluna do
  # veículo) e as já classificadas (não-manuais).
  # Status por veículo do motorista (fonte única no model: Consolidacao#veiculos_do_motorista).
  def veiculos_status(motorista)
    @consolidacao.veiculos_do_motorista(motorista)
  end

  # Valida que a entrega pertence ao motorista informado e ao período/veículo da
  # consolidação — impede classificar tracking_ids arbitrários via requisição.
  def entrega_elegivel?(motorista, tracking_id)
    motorista_na_consolidacao?(motorista) &&
      tracking_id.present? &&
      tracking_ids_elegiveis(motorista).include?(tracking_id)
  end

  # Pilares (tipos) selecionados no modal — aceita tipos[] (vários) ou tipo (um).
  def tipos_param
    tipos = Array(params[:tipos]).presence || Array(params[:tipo])
    tipos.reject(&:blank?)
  end

  def render_apontamento_erro(msg)
    respond_to do |format|
      format.json { render json: { ok: false, erro: msg }, status: :unprocessable_entity }
      format.html { redirect_to wizard_consolidacao_path(@consolidacao), alert: msg }
    end
  end

  def render_nao_elegivel
    respond_to do |format|
      format.json { render json: { ok: false, erro: 'Entrega não elegível para este motorista.' }, status: :unprocessable_entity }
      format.html do
        redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: params[:motorista]),
                    alert: 'Entrega não elegível para este motorista.'
      end
    end
  end

  def valor_para_tipo(tipo)
    ConsolidacaoEntrega.valor_para(tipo)
  end

  # Valor a aplicar na classificação. Para o pilar "extraordinária" o admin pode
  # informar um valor customizado (params[:valor]) por ser uma exceção; se vier
  # ausente/inválido, cai no preço padrão de Configurações. Demais pilares usam
  # sempre o preço configurado.
  def valor_classificacao(tipo)
    return valor_para_tipo(tipo) unless tipo.to_s == 'extraordinaria'

    valor = params[:valor].to_s.strip.tr(',', '.')
    valor = BigDecimal(valor) rescue nil
    valor.present? && valor >= 0 ? valor : Configuracao.preco_extraordinaria
  end

  # Valor customizado da Extraordinária vindo do formulário (params[:valor]),
  # aceitando vírgula decimal. Retorna nil se ausente/inválido — aí o model usa
  # o preço padrão de Configurações.
  def valor_extraordinaria_param
    bruto = params[:valor].to_s.strip.tr(',', '.')
    return nil if bruto.blank?

    valor = BigDecimal(bruto) rescue nil
    valor if valor && valor >= 0
  end

  def recalcular_motorista(nome)
    @consolidacao.recalcular_motorista!(nome)
  end

  # Conta lançamentos manuais como ENTREGAS: cada termo vale `quantidade` (lote
  # numa só linha); os demais apontamentos manuais valem 1 por nota (tracking).
  def contar_manuais(rel_manual)
    tipos_termo = ConsolidacaoEntrega.tipos.values_at(:termo, :termo_especial)
    rel_manual.where(tipo: tipos_termo).sum(:quantidade) +
      rel_manual.where.not(tipo: tipos_termo).distinct.count(:tracking_id)
  end

  # `vehicles` (opcional, string ou array): quando presente, TODO o resumo
  # (barra de progresso, contagem por pilar e valor total) reflete apenas o(s)
  # veículo(s) selecionado(s). `veiculos_status` acompanha o payload para o front
  # atualizar os chips sem recarregar.
  def resumo_json(motorista, vehicles: nil)
    lista          = veiculos_param(vehicles)
    classificacoes = @consolidacao.consolidacao_entregas.where(motorista_nome: motorista)
    status         = veiculos_status(motorista)
    classificadas_motorista = classificacoes.distinct.count(:tracking_id)

    if lista.any?
      # Recorta as classificações às NFs dos veículos selecionados.
      ids            = tracking_ids_elegiveis(motorista, lista)
      classificacoes = classificacoes.where(tracking_id: ids)
      escopo         = status.select { |v| lista.include?(v[:vehicle]) }.sum { |v| v[:classificadas] }
    else
      escopo = classificadas_motorista
    end

    {
      ok: true,
      classificadas:        classificadas_motorista,
      classificadas_escopo: escopo,
      manuais:              contar_manuais(classificacoes.where(manual: true)),
      por_tipo:             classificacoes.group(:tipo).sum(:quantidade),
      valor_total:          classificacoes.sum { |e| e.tipo == 'desconto' ? -e.valor_aplicado : e.valor_aplicado }.to_f,
      veiculos_status:      status
    }
  end
end
