# app/models/entrega.rb
#
# Model de LEITURA para a tabela existente da Reem Transporte.
# NUNCA criar migration para esta tabela.
# NUNCA executar INSERT, UPDATE, DELETE ou DROP nesta tabela.
#
class Entrega < ApplicationRecord
  self.table_name  = 'db_reem_simplerout_2026'
  self.primary_key = 'tracking_id'

  # Apenas leitura — segurança contra mutações acidentais
  def readonly?
    true
  end

  # Status que o SimpleRoute grava para entregas SEM SUCESSO (falha/insucesso).
  # AJUSTE aqui se o valor real for diferente (ex.: 'cancelled', 'not_delivered').
  STATUS_FALHA = %w[failed].freeze

  # Status em que o motorista FOI até o local: sucesso ('completed') ou
  # insucesso ('failed'). Base da elegibilidade na consolidação — o motorista
  # se deslocou, então a entrega precisa constar para ser classificada, mesmo
  # que não tenha sido entregue com sucesso.
  STATUS_ATENDIDO = (%w[completed] + STATUS_FALHA).freeze

  # ── Scopes ──────────────────────────────────────────────────
  scope :concluidas,    -> { where(status: 'completed') }
  scope :com_checkout,  -> { where.not(checkout: nil) }
  # Pagamento = entrega concluída com checkout registrado
  scope :pagas,         -> { concluidas.com_checkout }
  # Atendidas = motorista foi ao local (sucesso OU insucesso) com checkout.
  # É o conjunto elegível da consolidação (mais amplo que .pagas).
  scope :atendidas,     -> { where(status: STATUS_ATENDIDO).com_checkout }
  # Falhadas = entregas que não tiveram sucesso (status de falha)
  scope :falhadas,      -> { where(status: STATUS_FALHA) }
  # Pendentes = nem concluídas nem falhadas (ainda em aberto). Exclui as falhadas
  # para que entregues/pendentes/falhadas não se sobreponham no dashboard.
  scope :pendentes,     -> { where.not(status: ['completed'] + STATUS_FALHA) }

  # planned_date é timestamp: usamos o dia inteiro do limite superior para não
  # descartar as entregas do último dia do período (causava dashboard zerado e
  # subcontagem de elegíveis nas consolidações).
  scope :no_periodo, ->(inicio, fim) {
    where(planned_date: inicio.to_date.beginning_of_day..fim.to_date.end_of_day)
  }

  # Período pela DATA REAL da entrega (checkout) — usada nas análises financeiras
  # ("quando a entrega foi concluída"). Comparação naïve com Date (intervalo
  # meio-aberto: >= início e < fim+1), SEM conversão de fuso — bate com a análise
  # feita direto no banco. Só faz sentido sobre entregas .pagas (checkout != nil).
  scope :no_periodo_checkout, ->(inicio, fim) {
    where('checkout >= ? AND checkout < ?', inicio.to_date, fim.to_date + 1)
  }

  scope :do_motorista, ->(nome) {
    where(driver: nome)
  }

  # Busca pela NF (reference_id). A coluna é numérica, então comparamos como
  # texto para aceitar a entrada do usuário de forma robusta. Usado no
  # apontamento manual de entregas (NF que chegou fora do período).
  scope :por_nf, ->(nf) {
    where('reference_id::text = ?', nf.to_s.strip)
  }

  scope :da_veiculo, ->(vehicle) {
    where(vehicle: vehicle)
  }

  # Restringe às entregas cujas NFs (reference_id) constam na(s) tabela(s) de
  # operação informada(s). O JOIN é o mesmo do script de gestão do cliente:
  # reference_id::text = <tabela_operacao>.nota_fiscal
  #
  # SEGURANÇA: o nome da tabela entra no SQL, então passa pela whitelist
  # (Operacao.sanitizar) e por quote_table_name. Nomes inválidos são descartados;
  # se nada sobrar, não filtra (all) — o filtro de operação é opcional.
  scope :da_operacoes, ->(tabelas) {
    validas = Operacao.sanitizar(tabelas)
    next all if validas.empty?

    selects = validas.map { |t| "SELECT nota_fiscal FROM #{connection.quote_table_name(t)}" }
    where("reference_id::text IN (#{selects.join(' UNION ')})")
  }

  scope :do_mes, ->(data = Date.current) {
    where(planned_date: data.beginning_of_month..data.end_of_month)
  }

  # Filtra pela(s) conta(s) configurada(s) em DB_EXISTING_ACCOUNT_ID.
  # A coluna account_id é TEXTO e pode vir vazia/NULL, então comparamos como string.
  # IMPORTANTE: o valor é uma LISTA separada por vírgula — NÃO um trecho de SQL.
  # Aceita: "95907" (uma) | "95907,12345" (várias) | "all" ou vazio (sem filtro).
  # Use "95907," (vírgula no fim) para incluir também registros de conta vazia/NULL.
  scope :da_conta_gade, -> {
    valores = contas_gade
    valores.nil? ? all : where(account_id: valores)
  }

  # ── Métodos de classe ────────────────────────────────────────

  # Contas aceitas (DB_EXISTING_ACCOUNT_ID), normalizadas. `nil` = sem filtro
  # ("all"/vazio); um token vazio ("95907,") inclui também as linhas sem conta.
  def self.contas_gade
    contas = ENV.fetch('DB_EXISTING_ACCOUNT_ID', '95907').to_s.strip
    return nil if contas.empty? || contas.casecmp?('all')

    valores = contas.split(',', -1).map(&:strip)
    valores << nil if valores.any?(&:empty?)
    valores.uniq
  end

  # A MESMA condição do scope :da_conta_gade, como fragmento SQL — para as
  # queries cruas de Analytics (que montam UNION por operação e não passam pelo
  # ActiveRecord) usarem exatamente o mesmo recorte de conta do dashboard.
  # Devolve nil quando não há filtro. `apelido` é o alias da tabela na query.
  def self.condicao_conta_sql(apelido = table_name)
    valores = contas_gade
    return nil if valores.nil?

    coluna  = "#{connection.quote_table_name(apelido)}.account_id"
    listadas = valores.compact
    partes  = []
    partes << sanitize_sql_array(["#{coluna} IN (?)", listadas]) if listadas.any?
    partes << "#{coluna} IS NULL" if valores.include?(nil)
    "(#{partes.join(' OR ')})"
  end

  # Lista motoristas únicos (para selects, consolidações)
  def self.motoristas_ativos(inicio: nil, fim: nil)
    base = da_conta_gade
    base = base.no_periodo(inicio, fim) if inicio && fim
    base.distinct.order(:driver).pluck(:driver).compact.reject(&:empty?)
  end

  # Lista veículos únicos (para selects/filtros de consolidação)
  def self.veiculos_unicos(inicio: nil, fim: nil)
    base = da_conta_gade
    base = base.no_periodo(inicio, fim) if inicio && fim
    base.distinct.order(:vehicle).pluck(:vehicle).compact.reject(&:empty?)
  end

  # Veículos usados pelo(s) motorista(s) selecionado(s) no período — alimenta o
  # filtro dinâmico de veículos na criação de consolidação.
  #
  # ⚠️ Precisa usar EXATAMENTE o mesmo recorte da elegibilidade
  # (`atendidas` + `no_periodo_checkout`, ver contar_atendidas e
  # Consolidacao#veiculos_do_motorista). Usava `no_periodo` (planned_date), e o
  # descasamento sumia com veículo do filtro: entrega planejada em 31/07 e
  # entregue em 01/08 é elegível numa consolidação 01/08–14/08 (checkout dentro),
  # mas o veículo dela não aparecia (planned_date fora) — sem opção de marcar,
  # a entrega ficava de fora do fechamento SEM aviso, e o financeiro não batia.
  def self.veiculos_de_motoristas(motoristas, inicio: nil, fim: nil)
    nomes = Array(motoristas).reject(&:blank?)
    return [] if nomes.empty?

    base = da_conta_gade.where(driver: nomes)
    base = base.atendidas.no_periodo_checkout(inicio, fim) if inicio.present? && fim.present?
    base.distinct.order(:vehicle).pluck(:vehicle).compact.reject(&:empty?)
  end

  # Contagem de entregas ATENDIDAS (motorista foi ao local: sucesso + insucesso)
  # elegíveis para a consolidação.
  # Período pela DATA REAL da entrega (checkout) — ver no_periodo_checkout.
  def self.contar_atendidas(inicio:, fim:, motorista: nil, vehicle: nil, operacoes: nil)
    base = atendidas.da_conta_gade.no_periodo_checkout(inicio, fim)
    base = base.do_motorista(motorista)  if motorista.present?
    base = base.da_veiculo(vehicle)      if vehicle.present?
    base = base.da_operacoes(operacoes)  if operacoes.present?
    base.count
  end

  # ── Helpers de instância ─────────────────────────────────────

  def numero_nf
    reference_id
  end

  # Resumo para o apontamento manual (modal de busca de NF). Fonte única usada
  # pelos dois fluxos: apontar em consolidação existente e criar "nota avulsa".
  def resumo_apontamento
    {
      tracking_id: tracking_id,
      nf:          numero_nf,
      motorista:   driver,
      veiculo:     vehicle,
      local:       local,
      status:      status,
      pago:        elegivel_pagamento?,
      data:        (checkout || planned_date)&.to_s
    }
  end

  def local
    contact_name.presence || address
  end

  def concluida?
    status == 'completed'
  end

  # Insucesso: o motorista foi ao local mas a entrega não foi concluída.
  def falhada?
    STATUS_FALHA.include?(status)
  end

  def checkout_registrado?
    checkout.present?
  end

  def elegivel_pagamento?
    concluida? && checkout_registrado?
  end

  def atraso_minutos
    return 0 unless delay.present?
    delay.to_s.split(':').then { |h, m, _s| h.to_i * 60 + m.to_i }
  end
end
