# app/controllers/operacoes_dashboard_controller.rb
#
# Dashboard de análise de ENTREGAS por operação (separado do dashboard
# financeiro). O operador escolhe uma operação (UBS Norte, EMAD, ...) e vê os
# KPIs/insucessos/STS/mapa; também há os modos "global" (todas no período) e
# "comparar" (uma operação vs outra). Os números vêm de Analytics::OperacaoMetricas.
class OperacoesDashboardController < ApplicationController
  MODOS = %w[operacao global comparar].freeze

  # Linhas por página da tabela espelho (planilha da operação)
  ESPELHO_POR_PAGINA = 15

  XLSX_MIME = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'.freeze

  def index
    authorize :dashboard, :operacoes?

    @periodo_inicio, @periodo_fim = periodo_selecionado
    @periodo_explicito   = periodo_explicito?
    @modo                = MODOS.include?(params[:modo]) ? params[:modo] : 'operacao'
    @operacoes_agrupadas = Operacao.agrupadas_por_mes
    @tabelas_validas     = Operacao.nomes_validos
    @filtros, @chips     = cross_filtros
    @data_filtro         = parse_data(params[:f_data])  # clique no gráfico "Entregas por Dia"

    case @modo
    when 'global'
      # Global: agrega TODAS as operações dentro da faixa de datas escolhida.
      @metricas = montar(@tabelas_validas, inicio: @periodo_inicio, fim: @periodo_fim, filtros: @filtros, data: @data_filtro)
      @fora_operacao = Analytics::NotasForaOperacao.new(inicio: @periodo_inicio, fim: @periodo_fim)
    when 'comparar'
      # Comparar: cada operação inteira (sem filtro de data — só vale no Global).
      @op_a = Operacao.sanitizar([params[:op_a]]).first || @tabelas_validas[0]
      @op_b = Operacao.sanitizar([params[:op_b]]).first || @tabelas_validas[1]
      @metricas_a = montar([@op_a]) if @op_a
      @metricas_b = montar([@op_b]) if @op_b
    else
      # Operação única: o recorte é SEMPRE a operação inteira, mesmo com
      # inicio/fim na URL. É o que confere com os documentos físicos e com a
      # planilha entregue ao cliente (Analytics::PlanilhaEntregas também não
      # filtra data). Recortar pelo período derrubava as entregas que a operação
      # executou fora do mês do nome dela — EMAD AGO 2026 rodou de 30/07 a
      # 12/08, e "01/08 → hoje" mostrava 1146 das 2021 NFs.
      #
      # O período escolhido continua tendo função aqui: alimenta as "Notas fora
      # da operação" e aparece no cabeçalho como REFERÊNCIA (quantas NFs da
      # operação caíram na faixa), para comparar com o dashboard financeiro sem
      # mexer no número que o cliente confere.
      @operacao = Operacao.sanitizar([params[:operacao]]).first || @tabelas_validas.first
      @metricas = montar([@operacao], filtros: @filtros, data: @data_filtro) if @operacao
      @fora_operacao = Analytics::NotasForaOperacao.new(inicio: @periodo_inicio, fim: @periodo_fim) if @periodo_explicito
    end
  end

  # Planilha da operação (tabela espelho): página própria com busca e paginação.
  # Aceita os mesmos parâmetros do index (operacao/modo global/período/cross-
  # filters), então o botão no dashboard preserva o contexto atual.
  def planilha
    authorize :dashboard, :operacoes?

    @periodo_inicio, @periodo_fim = periodo_selecionado
    @periodo_explicito   = periodo_explicito?
    @modo                = params[:modo] == 'global' ? 'global' : 'operacao'
    @operacoes_agrupadas = Operacao.agrupadas_por_mes
    @tabelas_validas     = Operacao.nomes_validos
    @filtros, @chips     = cross_filtros
    @data_filtro         = parse_data(params[:f_data])

    if @modo == 'global'
      @metricas = montar(@tabelas_validas, inicio: @periodo_inicio, fim: @periodo_fim, filtros: @filtros, data: @data_filtro)
    else
      # Mesma regra do #index: operação = operação inteira (a planilha espelho
      # tem que bater com a que é entregue ao cliente).
      @operacao = Operacao.sanitizar([params[:operacao]]).first || @tabelas_validas.first
      @metricas = montar([@operacao], filtros: @filtros, data: @data_filtro) if @operacao
    end

    montar_tabela_espelho if @metricas
  end

  # GET /dashboard/operacoes/planilha/baixar?operacao=gade_entregas_...
  # Baixa a planilha Entregas (.xlsx) da operação já preenchida com o resultado
  # do rastreio — o modelo que é entregue ao cliente.
  def baixar_planilha
    # Chave própria: aqui o dado do cliente SAI do sistema (.xlsx), o que é
    # diferente de olhar os números na tela.
    authorize :dashboard, :exportar?

    tabela = params[:operacao].to_s
    unless Operacao.valida?(tabela)
      return redirect_to(operacoes_planilha_path, alert: 'Selecione uma operação válida.')
    end

    planilha = Analytics::PlanilhaEntregas.new(tabela)
    if planilha.linhas.empty?
      return redirect_to(operacoes_planilha_path(operacao: tabela),
                         alert: "A operação #{planilha.label} não tem registros.")
    end

    send_data Analytics::PlanilhaEntregasXlsx.new(planilha).gerar,
              filename: "Entregas #{planilha.label} #{Date.current.strftime('%m.%Y')}.xlsx",
              type: XLSX_MIME,
              disposition: 'attachment'
  end

  private

  # Tabela espelho da planilha da operação: busca textual (q) + paginação (pg)
  # sobre as linhas já cross-filtradas. Ordena pelas mais recentes (checkout;
  # sem checkout vai para o fim).
  def montar_tabela_espelho
    @busca = params[:q].to_s.strip
    linhas = @metricas.buscar(@busca).sort_by { |r| r['checkout'].to_s }.reverse

    @espelho_total   = linhas.size
    @espelho_paginas = [(@espelho_total / ESPELHO_POR_PAGINA.to_f).ceil, 1].max
    @espelho_pagina  = params[:pg].to_i.clamp(1, @espelho_paginas)
    @espelho_linhas  = linhas[(@espelho_pagina - 1) * ESPELHO_POR_PAGINA, ESPELHO_POR_PAGINA] || []
  end

  # Período escolhido pelo operador (veio na URL) x default da tela. No modo
  # Operação ele não recorta os KPIs — ver o comentário no #index.
  def periodo_explicito?
    params[:inicio].present? || params[:fim].present?
  end

  def montar(tabelas, inicio: nil, fim: nil, filtros: {}, data: nil)
    Analytics::OperacaoMetricas.new(tabelas: tabelas, inicio: inicio, fim: fim, filtros: filtros, data: data)
  end

  # Cross-filter por clique nas tabelas. Cada param vira um filtro de coluna e um
  # "chip" removível na tela. Retorna [filtros (coluna=>valor), chips (p/ a view)].
  CROSS = {
    'f_driver'    => { coluna: 'driver',       rotulo: 'Motorista' },
    'f_sts'       => { coluna: 'contact_name', rotulo: 'Unidade' },
    'f_status'    => { coluna: 'status_gade',  rotulo: 'Status' },
    'f_obs'       => { coluna: 'observation',  rotulo: 'Observação' },
    'f_resultado' => { coluna: 'status',       rotulo: 'Resultado' }
  }.freeze

  # Rótulos amigáveis para o filtro de Resultado (clique no donut "Insucessos %").
  RESULTADO_LABEL = { 'completed' => 'Completas', 'failed' => 'Falhas' }.freeze

  def cross_filtros
    filtros = {}
    chips   = []
    CROSS.each do |param, cfg|
      valor = params[param].to_s.strip
      next if valor.empty?
      filtros[cfg[:coluna]] = valor
      display = param == 'f_resultado' ? (RESULTADO_LABEL[valor] || valor) : valor
      chips << { param: param, valor: valor, display: display, rotulo: cfg[:rotulo] }
    end
    # Filtro por DIA (clique no gráfico "Entregas por Dia") — tratado à parte (data).
    if (dia = parse_data(params[:f_data]))
      chips << { param: 'f_data', valor: params[:f_data].to_s, display: dia.strftime('%d/%m/%Y'), rotulo: 'Dia' }
    end
    [filtros, chips]
  end

  # Faixa de datas do filtro (params inicio/fim). Default: início do mês → hoje.
  # Espelha DashboardController#periodo_selecionado.
  def periodo_selecionado
    inicio = parse_data(params[:inicio]) || Date.current.beginning_of_month
    fim    = parse_data(params[:fim])    || Date.current
    inicio, fim = fim, inicio if fim < inicio
    [inicio, fim]
  end

  def parse_data(str)
    return nil if str.blank?
    Date.parse(str)
  rescue ArgumentError, TypeError
    nil
  end
end
