import { prisma } from '../config/database.js';

export interface PlanLimits {
  maxRfcs: number;
  maxUsers: number;
  timbresIncluidosMes: number;
  features: string[];
}

export interface AddonDelta {
  maxRfcs?: number;
  maxUsers?: number;
  timbresIncluidosMes?: number;
  features?: string[];
}

export interface DespachoPlanLimits {
  plan: string;
  nombre: string;
  monthly: number | null;
  firstYear: number | null;
  renewal: number | null;
  permiteMonthly: boolean;
  maxRfcs: number;
  maxUsers: number;
  timbresIncluidosMes: number;
  dbMode: 'BYO' | 'MANAGED';
  permiteServidorBackup: boolean;
  permiteSatIncremental: boolean;
}

/** Suma deltas de addons activos sobre los limits base de un plan. -1 = ilimitado se preserva. */
export function computeEffectiveLimits(baseLimits: PlanLimits, addonDeltas: AddonDelta[]): PlanLimits {
  const result: PlanLimits = {
    maxRfcs: baseLimits.maxRfcs,
    maxUsers: baseLimits.maxUsers,
    timbresIncluidosMes: baseLimits.timbresIncluidosMes,
    features: [...baseLimits.features],
  };

  for (const delta of addonDeltas) {
    if (delta.maxRfcs) {
      result.maxRfcs = result.maxRfcs === -1 ? -1 : result.maxRfcs + delta.maxRfcs;
    }
    if (delta.maxUsers) {
      result.maxUsers = result.maxUsers === -1 ? -1 : result.maxUsers + delta.maxUsers;
    }
    if (delta.timbresIncluidosMes) {
      result.timbresIncluidosMes += delta.timbresIncluidosMes;
    }
    if (delta.features) {
      for (const f of delta.features) {
        if (!result.features.includes(f)) result.features.push(f);
      }
    }
  }

  return result;
}

// ============================================================================
// Catálogo despacho — lee de despacho_plan_prices con cache 5min
// ============================================================================

const CACHE_TTL_MS = 5 * 60 * 1000;
let cacheData: Map<string, DespachoPlanLimits> | null = null;
let cacheExpiresAt = 0;

async function loadCache(): Promise<Map<string, DespachoPlanLimits>> {
  const rows = await prisma.despachoPlanPrice.findMany();
  const map = new Map<string, DespachoPlanLimits>();
  for (const r of rows) {
    map.set(r.plan, {
      plan: r.plan,
      nombre: r.nombre,
      monthly: r.monthly !== null ? Number(r.monthly) : null,
      firstYear: r.firstYear !== null ? Number(r.firstYear) : null,
      renewal: r.renewal !== null ? Number(r.renewal) : null,
      permiteMonthly: r.permiteMonthly,
      maxRfcs: r.maxRfcs,
      maxUsers: r.maxUsers,
      timbresIncluidosMes: r.timbresIncluidosMes,
      dbMode: r.dbMode as 'BYO' | 'MANAGED',
      permiteServidorBackup: r.permiteServidorBackup,
      permiteSatIncremental: r.permiteSatIncremental,
    });
  }
  cacheData = map;
  cacheExpiresAt = Date.now() + CACHE_TTL_MS;
  return map;
}

/** Invalida el cache. Llamar tras editar precios/limits desde admin. */
export function invalidateDespachoPlanCache(): void {
  cacheData = null;
  cacheExpiresAt = 0;
}

/** Lee limits + precios de un plan despacho. Cache 5min. */
export async function getDespachoPlanLimits(plan: string): Promise<DespachoPlanLimits | null> {
  if (!cacheData || Date.now() > cacheExpiresAt) await loadCache();
  return cacheData!.get(plan) ?? null;
}

/** Lee todos los planes despacho. Cache 5min. */
export async function getAllDespachoPlanLimits(): Promise<DespachoPlanLimits[]> {
  if (!cacheData || Date.now() > cacheExpiresAt) await loadCache();
  return Array.from(cacheData!.values());
}

/** True si el plan acepta frecuencia mensual. Lee de BD via cache. */
export async function permiteFrecuenciaMensualDb(plan: string): Promise<boolean> {
  const cfg = await getDespachoPlanLimits(plan);
  return cfg?.permiteMonthly ?? false;
}

/** True si el plan cobra distinto en el primer año vs renovaciones. Lee de BD. */
export async function despachoPlanTieneDualidadDb(plan: string): Promise<boolean> {
  const cfg = await getDespachoPlanLimits(plan);
  if (!cfg || cfg.firstYear === null || cfg.renewal === null) return false;
  return cfg.firstYear !== cfg.renewal;
}

/**
 * Resuelve el precio MXN para un (plan, frequency, phase) leyendo de BD.
 * Throws si el plan no existe en BD o no permite la frecuencia solicitada.
 */
export async function getPrecioDespachoDb(
  plan: string,
  frequency: 'monthly' | 'annual',
  phase: 'firstYear' | 'renewal' = 'renewal',
): Promise<number> {
  const cfg = await getDespachoPlanLimits(plan);
  if (!cfg) throw new Error(`Plan ${plan} no encontrado en catálogo BD`);
  if (frequency === 'monthly') {
    if (!cfg.permiteMonthly || cfg.monthly === null) {
      throw new Error(`El plan ${plan} no permite frecuencia mensual`);
    }
    return cfg.monthly;
  }
  const price = phase === 'firstYear' ? cfg.firstYear : cfg.renewal;
  if (price === null) throw new Error(`El plan ${plan} no tiene precio anual configurado`);
  return price;
}

// ============================================================================
// Endpoints viejos — backward compat con /plan-catalogo/* routes
// (sin callers frontend conocidos; se mantienen por si algo externo consume)
// ============================================================================

export async function listPlans(_verticalProfile?: string) {
  const all = await getAllDespachoPlanLimits();
  // Excluir trial y custom del catálogo público (admin-only)
  return all
    .filter(p => p.plan !== 'trial' && p.plan !== 'custom')
    .map(p => ({
      codename: p.plan,
      nombre: p.nombre,
      verticalProfile: 'CONTABLE' as const,
      precioBase: p.firstYear ?? 0,
      frecuencia: p.permiteMonthly ? 'mensual' : 'anual',
      limits: {
        maxRfcs: p.maxRfcs,
        maxUsers: p.maxUsers,
        timbresIncluidosMes: p.timbresIncluidosMes,
        features: [], // features viven en TS (DESPACHO_PLANS); este endpoint no las expone
      } as PlanLimits,
    }));
}

export async function getPlanByCodename(codename: string) {
  const p = await getDespachoPlanLimits(codename);
  if (!p) return null;
  return {
    codename: p.plan,
    nombre: p.nombre,
    verticalProfile: 'CONTABLE' as const,
    precioBase: p.firstYear ?? 0,
    frecuencia: p.permiteMonthly ? 'mensual' : 'anual',
    limits: {
      maxRfcs: p.maxRfcs,
      maxUsers: p.maxUsers,
      timbresIncluidosMes: p.timbresIncluidosMes,
      features: [],
    } as PlanLimits,
  };
}

export async function listAddons(verticalProfile?: string) {
  const where: any = { active: true };
  if (verticalProfile) {
    where.OR = [
      { verticalProfile },
      { verticalProfile: null },
    ];
  }
  const addons = await prisma.planAddonCatalogo.findMany({
    where,
    orderBy: { precio: 'asc' },
  });
  return addons.map(a => ({
    id: a.id,
    codename: a.codename,
    nombre: a.nombre,
    verticalProfile: a.verticalProfile,
    precio: Number(a.precio),
    frecuencia: a.frecuencia,
    delta: a.delta as AddonDelta,
  }));
}
