/* ============================================================
 * Copyright (c) 2026 RAVAPI Soluções. www.ravapi.com
 * Licença (Klavo) — o que pode e o que não pode quando ela vence
 * ============================================================ */

import { emTransacao, type Pool } from '../db/client.ts';
import { LicencaBloqueada, LimiteDoPlano } from '../core/errors.ts';

export type EstadoLicenca = 'ATIVA' | 'EXPIRANDO' | 'GRACE' | 'BLOQUEADA' | 'REVOGADA';

export interface Licenca {
  status: EstadoLicenca;
  plano: string;
  modo: 'saas' | 'embarcado';
  limites: { empresas?: number; usuarios?: number; recibos_mes?: number; series?: number };
  validoAte: Date | null;
  graceDias: number;
  emTrial: boolean;
}

/**
 * Tolerância efetiva.
 *
 * Trial de 15 dias somado a tolerância de 15 dias daria 30 dias grátis. A
 * tolerância existe para proteger cliente adimplente de problema de rede ou
 * boleto atrasado — aplicada a um trial vencido, ela vira extensão automática
 * do próprio trial.
 *
 * O Klavo emite grace_dias = 0 no trial. Isto aqui é a trava do lado do
 * produto: configuração errada lá não vira trial de um mês aqui.
 */
export function toleranciaEfetiva(graceDias: number, emTrial: boolean): number {
  return emTrial ? 0 : graceDias;
}

/**
 * Bloquear a emissão de quem está em atraso impede o cliente de cumprir
 * obrigação com terceiros; bloquear a leitura seria reter dado que é dele.
 * Por isso só a criação de coisa nova para. Tudo que já existe continua
 * acessível, sempre — inclusive a exportação completa.
 */
export function podeSelar(l: Licenca): boolean {
  return l.status !== 'BLOQUEADA' && l.status !== 'REVOGADA';
}
export function podeLer(): boolean { return true; }
export function podeImprimir(): boolean { return true; }
export function podeExportar(): boolean { return true; }
export function podeCancelar(): boolean { return true; }

export function estadoPorData(validoAte: Date | null, graceDias: number, agora = new Date()): EstadoLicenca {
  if (!validoAte) return 'BLOQUEADA';
  const fimGrace = new Date(validoAte.getTime() + graceDias * 86_400_000);
  const avisoEm = new Date(validoAte.getTime() - 7 * 86_400_000);
  if (agora > fimGrace) return 'BLOQUEADA';
  if (agora > validoAte) return 'GRACE';
  if (agora >= avisoEm) return 'EXPIRANDO';
  return 'ATIVA';
}

export class LicencaService {
  constructor(private readonly pool: Pool) {}

  async atual(): Promise<Licenca> {
    return emTransacao(this.pool, async (x) => {
      const { rows } = await x.query('SELECT * FROM recibos.license_state LIMIT 1');
      const r = rows[0];
      if (!r) {
        // Sem licença registrada o sistema não inventa permissão.
        return {
          status: 'BLOQUEADA', plano: 'nenhum', modo: 'saas',
          limites: {}, validoAte: null, graceDias: 0, emTrial: false,
        };
      }
      const validoAte = r.valido_ate ? new Date(r.valido_ate) : null;
      const emTrial = Boolean(r.em_trial);
      const grace = toleranciaEfetiva(r.grace_dias, emTrial);
      return {
        status: r.status === 'REVOGADA' ? 'REVOGADA' : estadoPorData(validoAte, grace),
        plano: r.plano, modo: r.modo, limites: r.limites ?? {},
        validoAte, graceDias: grace, emTrial,
      };
    });
  }

  async exigirPodeSelar(): Promise<Licenca> {
    const l = await this.atual();
    if (!podeSelar(l)) throw new LicencaBloqueada();
    return l;
  }

  /** Consumo do mês corrente, para o limite e para o relatório ao Klavo. */
  async consumoDoMes(): Promise<number> {
    return emTransacao(this.pool, async (x) => {
      const { rows } = await x.query(
        `SELECT coalesce(recibos_selados, 0)::int AS n FROM recibos.usage_counters
          WHERE competencia = date_trunc('month', current_date)::date`,
      );
      return rows[0]?.n ?? 0;
    });
  }

  async exigirDentroDoLimiteMensal(l: Licenca): Promise<void> {
    const limite = l.limites.recibos_mes;
    if (!limite) return;
    const usados = await this.consumoDoMes();
    if (usados >= limite) throw new LimiteDoPlano('recibos por mês', limite);
  }
}
