# app/controllers/consolidacoes_controller.rb
class ConsolidacoesController < ApplicationController
  include Auditavel

  before_action :set_consolidacao,
                only: %i[show edit update destroy finalizar arquivar reativar wizard
                         registrar_pagamento cancelar_pagamento
                         preview_holerite gerar_pdf_relatorio gerar_pdf_holerite
                         gerar_pdf_financeiro]

  # Formas de pagamento aceitas (o select da UI usa esta mesma lista).
  FORMAS_PAGAMENTO = %w[pix transferencia dinheiro].freeze

  # GET /consolidacoes — lista com filtros
  def index
    authorize Consolidacao

    @consolidacoes = Consolidacao.ativas.recentes.includes(:criador)

    # Filtros
    @consolidacoes = @consolidacoes.where(status: params[:status])           if params[:status].present?
    @consolidacoes = @consolidacoes.where('nome ILIKE ?', "%#{params[:nome]}%") if params[:nome].present?
    @consolidacoes = @consolidacoes.where('data_inicio >= ?', params[:inicio]) if params[:inicio].present?
    @consolidacoes = @consolidacoes.where('data_fim <= ?', params[:fim])       if params[:fim].present?

    if params[:motorista].present?
      ids = ConsolidacaoMotorista.where('motorista_nome ILIKE ?', "%#{params[:motorista]}%")
                                 .pluck(:consolidacao_id)
      @consolidacoes = @consolidacoes.where(id: ids)
    end

    if params[:vehicle].present?
      @consolidacoes = @consolidacoes.where('vehicle_ids @> ?', [params[:vehicle]].to_json)
    end

    if %w[pago pendente parcial].include?(params[:pagamento])
      @consolidacoes = @consolidacoes.public_send("pagamento_#{params[:pagamento]}")
    end
  end

  # GET /consolidacoes/new
  def new
    authorize Consolidacao
    @consolidacao = Consolidacao.new(
      data_inicio: Date.current.beginning_of_month,
      data_fim:    Date.current
    )
    @motoristas = Entrega.motoristas_ativos
    @veiculos   = Entrega.veiculos_unicos
    @operacoes  = Operacao.todas
  end

  # POST /consolidacoes
  def create
    authorize Consolidacao
    @consolidacao = Consolidacao.new(consolidacao_params.merge(created_by: current_user.id))
    @consolidacao.operacoes = Operacao.sanitizar(@consolidacao.operacoes)

    motoristas = Array(params[:motoristas]).reject(&:blank?)

    if motoristas.empty?
      @consolidacao.errors.add(:base, 'Selecione ao menos um motorista')
      preparar_form_new
      return render :new, status: :unprocessable_entity
    end

    ActiveRecord::Base.transaction do
      @consolidacao.save!
      motoristas.each do |nome|
        @consolidacao.consolidacao_motoristas.create!(motorista_nome: nome)
      end
    end

    auditar!(:criar, @consolidacao, dados_novos: @consolidacao.attributes)
    redirect_to wizard_consolidacao_path(@consolidacao), notice: 'Consolidação criada! Selecione o motorista para validar.'
  rescue ActiveRecord::RecordInvalid
    preparar_form_new
    render :new, status: :unprocessable_entity
  end

  # GET /consolidacoes/veiculos_disponiveis?motoristas[]=X&inicio=&fim=
  # Lista (JSON) os veículos usados pelos motoristas selecionados no período —
  # alimenta o filtro dinâmico de veículos no formulário de nova consolidação.
  def veiculos_disponiveis
    authorize Consolidacao, :new?
    veiculos = Entrega.veiculos_de_motoristas(
      params[:motoristas], inicio: params[:inicio], fim: params[:fim]
    )
    render json: { veiculos: veiculos }
  end

  # GET /consolidacoes/operacoes_disponiveis — lista (JSON) as tabelas de operação.
  def operacoes_disponiveis
    authorize Consolidacao, :new?
    render json: { operacoes: Operacao.todas }
  end

  # GET /consolidacoes/dados_da_operacao?operacoes[]=X — motoristas/veículos/período
  # derivados das operações selecionadas, para pré-preencher o formulário.
  def dados_da_operacao
    authorize Consolidacao, :new?
    render json: Operacao.dados(params[:operacoes])
  end

  # GET /consolidacoes/buscar_nf?nf=123 — busca a NF na SimpleRoute (nota avulsa).
  def buscar_nf
    authorize Consolidacao, :create?

    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/avulsa — cria uma consolidação JÁ a partir de uma NF
  # avulsa (entrega que chegou por fora) e adiciona o apontamento ao motorista.
  def avulsa
    authorize Consolidacao, :create?

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

    data_ref = (entrega.checkout || entrega.planned_date)&.to_date || Date.current

    consolidacao = nil
    ActiveRecord::Base.transaction do
      consolidacao = Consolidacao.create!(
        nome:        "Avulsa nf#{entrega.numero_nf}",
        data_inicio: data_ref,
        data_fim:    data_ref.end_of_month,
        created_by:  current_user.id
      )
      consolidacao.adicionar_apontamento!(entrega, Array(params[:tipos]).reject(&:blank?), current_user)
    end

    auditar!(:criar, consolidacao,
             dados_novos: { avulsa_nf: entrega.numero_nf, motorista: entrega.driver, tipos: params[:tipos] })
    redirect_to consolidacao_path(consolidacao),
                notice: "Consolidação avulsa criada para #{entrega.driver} (NF #{entrega.numero_nf})."
  rescue ArgumentError => e
    redirect_to consolidacoes_path, alert: e.message
  rescue ActiveRecord::RecordInvalid => e
    redirect_to consolidacoes_path, alert: "Não foi possível criar a consolidação avulsa: #{e.message}"
  end

  # GET /consolidacoes/:id — visão geral
  def show
    authorize @consolidacao
    @motoristas = @consolidacao.consolidacao_motoristas.order(:motorista_nome)
    # Apontamentos manuais agrupados por nota (tracking) p/ exibição e remoção.
    @apontamentos = @consolidacao.consolidacao_entregas.apontamentos
                                 .order(:motorista_nome, :tracking_id)
                                 .group_by { |ce| [ce.motorista_nome, ce.tracking_id] }
  end

  # GET /consolidacoes/:id/wizard — PASSO 1: selecionar motorista
  def wizard
    authorize @consolidacao, :update?

    @motoristas = @consolidacao.consolidacao_motoristas.order(:motorista_nome).map do |cm|
      total         = @consolidacao.entregas_elegiveis_count(cm.motorista_nome)
      classificadas = @consolidacao.classificadas_count(cm.motorista_nome)
      apontamentos  = @consolidacao.consolidacao_entregas
                                   .apontamentos
                                   .where(motorista_nome: cm.motorista_nome)
                                   .distinct.count(:tracking_id)
      {
        registro:      cm,
        total:         total,
        classificadas: classificadas,
        apontamentos:  apontamentos,
        # Espelha o gate do servidor (todas_entregas_classificadas?): um motorista
        # só com apontamento manual tem 0 entregas elegíveis e já está "completo".
        completo:      total.zero? || classificadas >= total
      }
    end
  end

  # POST /consolidacoes/:id/finalizar
  def finalizar
    authorize @consolidacao, :update?

    if @consolidacao.finalizar!(current_user)
      auditar!(:finalizar, @consolidacao)
      NotificacaoService.notificar_finalizacao(@consolidacao)   # WhatsApp/email aos motoristas
      redirect_to @consolidacao, notice: 'Consolidação finalizada! Motoristas notificados.'
    else
      redirect_to wizard_consolidacao_path(@consolidacao),
                  alert: 'Não é possível finalizar: existem entregas não classificadas.'
    end
  end

  # POST /consolidacoes/:id/arquivar
  def arquivar
    authorize @consolidacao, :destroy?
    @consolidacao.arquivar!(current_user)
    auditar!(:arquivar, @consolidacao)
    redirect_to consolidacoes_path, notice: 'Consolidação arquivada.'
  end

  # GET /consolidacoes/arquivadas — histórico de consolidações arquivadas
  def arquivadas
    authorize Consolidacao
    @consolidacoes = Consolidacao.arquivadas.recentes.includes(:criador, :arquivador)
  end

  # POST /consolidacoes/:id/reativar — restaura uma consolidação arquivada
  def reativar
    authorize @consolidacao, :destroy?
    @consolidacao.reativar!(current_user)
    auditar!(:reativar, @consolidacao)
    redirect_to consolidacoes_path, notice: 'Consolidação reativada.'
  end

  # DELETE /consolidacoes/:id — exclui PERMANENTEMENTE (apenas arquivadas)
  def destroy
    authorize @consolidacao, :destroy?
    @consolidacao.destroy!
    auditar!(:excluir, @consolidacao)
    redirect_to arquivadas_consolidacoes_path, notice: 'Consolidação excluída permanentemente.'
  end

  # POST /consolidacoes/:id/registrar_pagamento
  # Marca 1 motorista (param consolidacao_motorista_id) ou TODOS os pendentes.
  def registrar_pagamento
    authorize @consolidacao, :registrar_pagamento?
    return pagamento_indisponivel unless @consolidacao.finalizada?

    forma = params[:forma_pagamento].presence_in(FORMAS_PAGAMENTO) || 'pix'
    alvos = motoristas_alvo.select { |cm| !cm.pago? }

    alvos.each do |cm|
      cm.marcar_pago!(current_user, forma: forma)
      NotificacaoService.notificar_pagamento(@consolidacao, cm)
    end

    auditar!(:registrar_pagamento, @consolidacao,
             dados_novos: { motoristas: alvos.map(&:motorista_nome), forma: forma })
    redirect_to @consolidacao, notice: pagamento_notice(alvos.size, :pago)
  end

  # POST /consolidacoes/:id/cancelar_pagamento — estorna 1 motorista ou TODOS.
  def cancelar_pagamento
    authorize @consolidacao, :cancelar_pagamento?

    alvos = motoristas_alvo.select(&:pago?)
    alvos.each { |cm| cm.cancelar_pagamento!(current_user) }

    auditar!(:cancelar_pagamento, @consolidacao,
             dados_novos: { motoristas: alvos.map(&:motorista_nome) })
    redirect_to @consolidacao, notice: pagamento_notice(alvos.size, :estornado)
  end

  # ── FASE 7 — PDFs ────────────────────────────────────────────

  # GET /consolidacoes/:id/gerar_pdf_relatorio?motorista=X
  def gerar_pdf_relatorio
    authorize @consolidacao, :show?
    motorista = params[:motorista]

    pdf = Pdf::RelatorioMotoristaPdf.new(consolidacao: @consolidacao, motorista: motorista)
    auditar!(:editar, @consolidacao, dados_novos: { pdf: 'relatorio', motorista: motorista })

    send_data pdf.render,
              filename:    "relatorio_#{motorista.parameterize}_#{@consolidacao.id}.pdf",
              type:        'application/pdf',
              disposition: 'attachment'
  end

  # GET /consolidacoes/:id/gerar_pdf_financeiro — relatório financeiro da consolidação
  def gerar_pdf_financeiro
    authorize @consolidacao, :show?

    pdf = Pdf::RelatorioFinanceiroConsolidacaoPdf.new(consolidacao: @consolidacao)
    auditar!(:editar, @consolidacao, dados_novos: { pdf: 'financeiro' })

    send_data pdf.render,
              filename:    "financeiro_#{@consolidacao.nome.parameterize}_#{@consolidacao.id}.pdf",
              type:        'application/pdf',
              disposition: 'attachment'
  end

  # GET /consolidacoes/:id/gerar_pdf_holerite?motorista=X
  def gerar_pdf_holerite
    autorizar_holerite!(params[:motorista])
    motorista = params[:motorista]
    user_mot  = buscar_user_motorista(motorista)
    user_mot&.regenerar_login_token! if user_mot&.login_token.blank?
    cm = @consolidacao.consolidacao_motoristas.find_by(motorista_nome: motorista)

    pdf = Pdf::HoleritePdf.new(consolidacao: @consolidacao, motorista: motorista,
                               user_motorista: user_mot, app_host: ENV['APP_HOST'],
                               pago_em: cm&.pago_em, forma_pagamento: cm&.forma_pagamento)
    auditar!(:editar, @consolidacao, dados_novos: { pdf: 'holerite', motorista: motorista })

    send_data pdf.render,
              filename:    "holerite_#{motorista.parameterize}_#{@consolidacao.id}.pdf",
              type:        'application/pdf',
              disposition: 'attachment'
  end

  # GET /consolidacoes/:id/preview_holerite?motorista=X — preview HTML antes do PDF
  def preview_holerite
    authorize @consolidacao, :show?
    @motorista = params[:motorista]
    @entregas  = @consolidacao.consolidacao_entregas.where(motorista_nome: @motorista)
    @resumo    = @entregas.group(:tipo).count
    @total     = @entregas.sum { |e| e.desconto? ? -e.valor_aplicado : e.valor_aplicado }
    render layout: false   # renderiza dentro do modal (Turbo Frame)
  end

  private

  # Acha o User motorista pelo nome para gerar o QR Code de acesso. O
  # motorista_nome vem dos dados externos de entrega e o users.nome é digitado
  # no admin — então toleramos diferenças de caixa e de espaços (extras/duplos).
  # Sem isso, qualquer divergência deixava user_mot nil e o QR sumia do holerite.
  def buscar_user_motorista(nome)
    exato = User.motorista.find_by('LOWER(nome) = ?', nome.to_s.downcase.strip)
    return exato if exato

    alvo = normalizar_nome(nome)
    User.motorista.find { |u| normalizar_nome(u.nome) == alvo }
  end

  def normalizar_nome(nome)
    nome.to_s.strip.downcase.gsub(/\s+/, ' ')
  end

  # Staff (show?) baixa qualquer holerite. O motorista baixa só o PRÓPRIO — o PDF
  # já é filtrado pelo nome dele, então não há vazamento de dados de terceiros.
  def autorizar_holerite!(motorista_nome)
    if current_user.motorista?
      raise Pundit::NotAuthorizedError unless motorista_nome.to_s.casecmp?(current_user.nome.to_s)
      skip_authorization
    else
      authorize @consolidacao, :show?
    end
  end

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

  # Um motorista específico (consolidacao_motorista_id) ou todos da consolidação.
  def motoristas_alvo
    if params[:consolidacao_motorista_id].present?
      [@consolidacao.consolidacao_motoristas.find(params[:consolidacao_motorista_id])]
    else
      @consolidacao.consolidacao_motoristas.to_a
    end
  end

  def pagamento_indisponivel
    redirect_to @consolidacao, alert: 'Só é possível registrar pagamento de uma consolidação finalizada.'
  end

  def pagamento_notice(qtd, acao)
    return 'Nenhuma alteração: motoristas já estavam no estado solicitado.' if qtd.zero?

    verbo = acao == :pago ? 'marcado(s) como pago(s)' : 'com pagamento estornado'
    "#{qtd} motorista(s) #{verbo}."
  end

  def preparar_form_new
    @motoristas = Entrega.motoristas_ativos
    @veiculos   = Entrega.veiculos_unicos
    @operacoes  = Operacao.todas
  end

  def consolidacao_params
    params.require(:consolidacao).permit(:nome, :data_inicio, :data_fim, vehicle_ids: [], operacoes: [])
  end
end
