import type { Request, Response, NextFunction } from 'express';
import { prisma } from '../config/database.js';
import { isGlobalAdmin } from '../utils/global-admin.js';
import { getSubscriptionState } from '@horux/shared';

// Simple in-memory cache with TTL
const cache = new Map<string, { data: any; expires: number }>();

async function getCached<T>(key: string, ttlMs: number, fetcher: () => Promise<T>): Promise<T> {
  const entry = cache.get(key);
  if (entry && entry.expires > Date.now()) return entry.data;
  const data = await fetcher();
  cache.set(key, { data, expires: Date.now() + ttlMs });
  return data;
}

export function invalidateTenantCache(tenantId: string) {
  for (const key of cache.keys()) {
    if (key.includes(tenantId)) cache.delete(key);
  }
}

/**
 * Verifica que el tenant tenga una suscripción que permita escritura.
 * GETs siempre pasan (modo lectura preserva acceso histórico para auditorías y export).
 *
 * La autoridad de "puede escribir" está en `getSubscriptionState().needsRenewal`
 * (helper compartido entre frontend y backend) — combina `status` + `currentPeriodEnd`,
 * por eso un trial cuyo período ya pasó pero el cron de las 02:30 AM aún no lo ha
 * marcado `trial_expired` queda bloqueado igual.
 */
export async function checkPlanLimits(req: Request, res: Response, next: NextFunction) {
  if (!req.user) return next();

  // Allow GET requests siempre — modo lectura habilitado incluso sin suscripción
  // activa (preserva acceso a dashboard/CFDIs históricos para auditorías).
  if (req.method === 'GET') return next();

  // Global admin impersonation bypasses subscription check
  if (req.headers['x-view-tenant'] && await isGlobalAdmin(req.user.tenantId, req.user.role)) {
    return next();
  }

  const subscription = await getCached(
    `sub:${req.user.tenantId}`,
    5 * 60 * 1000,
    () => prisma.subscription.findFirst({
      where: { tenantId: req.user!.tenantId },
      orderBy: { createdAt: 'desc' },
    })
  );

  const state = getSubscriptionState(subscription);

  if (state.needsRenewal) {
    return res.status(403).json({
      code: 'SUBSCRIPTION_INACTIVE',
      message: state.isTrialExpired
        ? 'Tu prueba gratuita terminó. Renueva tu plan para continuar.'
        : state.isCancelledExpired
          ? 'Tu suscripción venció. Renueva tu plan para continuar.'
          : 'No tienes una suscripción activa. Contrata un plan para continuar.',
      redirectTo: '/configuracion/planes-despacho',
      subscriptionStatus: state.status,
    });
  }

  next();
}

/**
 * No-op tras eliminar el catálogo Horux 360 legacy. En el modelo despacho el
 * límite de CFDIs es por contribuyente (`DESPACHO_PLANS[plan].maxCfdisPorContribuyente`,
 * 1M-3M), no por tenant — tan alto que no tiene sentido enforced en cada
 * insert. Si en el futuro se quiere gating real por contribuyente, debe
 * leerse del catálogo despacho con conocimiento del contribuyente_id que
 * está creando el CFDI (no disponible en este middleware tenant-level).
 */
export async function checkCfdiLimit(_req: Request, _res: Response, next: NextFunction) {
  return next();
}
