/**
 * Métricas para la página de Gestión de Clientes (admin global).
 *
 * Distinto de `admin-dashboard.service.ts` que provee KPIs generales fijos
 * a 30 días. Aquí el rango es parametrizado y se enfoca en suscripciones
 * activas por plan, ingresos del periodo, clientes que no renovaron, y
 * usuarios por cliente.
 */
import { prisma } from '../config/database.js';

export interface ClientesStatsRange {
  from: Date;
  to: Date;
}

export interface ClientesStats {
  /** Suscripciones activas (status=authorized) agrupadas por plan. */
  suscripcionesPorPlan: Array<{ plan: string; count: number }>;
  /** Ingresos del periodo (payments approved con createdAt en rango). */
  ingresos: {
    total: number;
    paymentsCount: number;
  };
  /** Clientes cuya suscripción venció en el rango y NO se renovó. */
  noRenovaciones: Array<{
    tenantId: string;
    tenantNombre: string;
    rfc: string;
    plan: string;
    currentPeriodEnd: string;
    statusActual: string;
  }>;
  /** Cuenta de usuarios activos por tenant. */
  usuariosPorCliente: Array<{
    tenantId: string;
    tenantNombre: string;
    rfc: string;
    activeUsers: number;
    owners: number;
  }>;
}

export async function getClientesStats(range: ClientesStatsRange): Promise<ClientesStats> {
  // 1) Suscripciones activas agrupadas por plan
  const subsByPlan = await prisma.subscription.groupBy({
    by: ['plan'],
    where: { status: 'authorized' },
    _count: { _all: true },
  });
  const suscripcionesPorPlan = subsByPlan.map(s => ({
    plan: String(s.plan),
    count: s._count._all,
  }));

  // 2) Ingresos del periodo
  const payments = await prisma.payment.aggregate({
    where: {
      status: 'approved',
      createdAt: { gte: range.from, lte: range.to },
    },
    _sum: { amount: true },
    _count: true,
  });
  const ingresos = {
    total: Number(payments._sum.amount || 0),
    paymentsCount: payments._count,
  };

  // 3) Clientes que NO renovaron:
  //    a) Subs cuyo currentPeriodEnd cae en el rango y están en status terminal
  //       (cancelled, trial_expired, paused).
  //    b) Tenants cuyo trialEndsAt ya pasó y NO tienen suscripción authorized
  //       (incluye trials que nunca convirtieron o cuya sub fue borrada).
  //    c) Tenants con sub trial vencida (currentPeriodEnd < ahora) que nunca
  //       fue marcada trial_expired por el cron.
  const subsExpiradas = await prisma.subscription.findMany({
    where: {
      currentPeriodEnd: { gte: range.from, lte: range.to },
      status: { in: ['cancelled', 'trial_expired', 'paused'] },
    },
    select: {
      tenantId: true,
      plan: true,
      status: true,
      currentPeriodEnd: true,
      tenant: { select: { id: true, nombre: true, rfc: true } },
    },
  });

  const noRenovacionesMap = new Map<string, ClientesStats['noRenovaciones'][number]>();
  for (const s of subsExpiradas) {
    noRenovacionesMap.set(s.tenantId, {
      tenantId: s.tenantId,
      tenantNombre: s.tenant?.nombre ?? '',
      rfc: s.tenant?.rfc ?? '',
      plan: String(s.plan),
      currentPeriodEnd: s.currentPeriodEnd?.toISOString() ?? '',
      statusActual: s.status,
    });
  }

  // b + c) Trials vencidos / sin suscripción activa / subs borradas
  const now = new Date();
  const tenantsConSubAutorizada = new Set(
    (await prisma.subscription.findMany({
      where: { status: 'authorized' },
      select: { tenantId: true },
    })).map(s => s.tenantId)
  );
  const excluded = Array.from(tenantsConSubAutorizada);

  // Tenants con trialEndsAt pasado y sin sub authorized
  const tenantsTrialsVencidos = await prisma.tenant.findMany({
    where: {
      trialEndsAt: { lt: now },
      id: { notIn: excluded },
    },
    select: { id: true, nombre: true, rfc: true, plan: true, trialEndsAt: true },
  });
  for (const t of tenantsTrialsVencidos) {
    if (noRenovacionesMap.has(t.id)) continue;
    noRenovacionesMap.set(t.id, {
      tenantId: t.id,
      tenantNombre: t.nombre,
      rfc: t.rfc ?? '',
      plan: String(t.plan ?? 'trial'),
      currentPeriodEnd: t.trialEndsAt?.toISOString() ?? '',
      statusActual: 'trial_expired',
    });
  }

  // Tenants con sub trial vencida (currentPeriodEnd < ahora) que nunca fue
  // marcada trial_expired por el cron, y no tienen otra sub authorized.
  const subsTrialVencidas = await prisma.subscription.findMany({
    where: {
      status: 'trial',
      currentPeriodEnd: { lt: now },
      tenantId: { notIn: excluded },
    },
    select: {
      tenantId: true,
      plan: true,
      currentPeriodEnd: true,
      tenant: { select: { id: true, nombre: true, rfc: true } },
    },
  });
  for (const s of subsTrialVencidas) {
    if (noRenovacionesMap.has(s.tenantId)) continue;
    noRenovacionesMap.set(s.tenantId, {
      tenantId: s.tenantId,
      tenantNombre: s.tenant?.nombre ?? '',
      rfc: s.tenant?.rfc ?? '',
      plan: String(s.plan),
      currentPeriodEnd: s.currentPeriodEnd?.toISOString() ?? '',
      statusActual: 'trial_expired',
    });
  }

  // Tenants con plan de pago asignado manualmente (plan != 'trial') pero
  // sin NINGUNA suscripción. Indica que nunca iniciaron el flujo de pago.
  const tenantsConPlanPeroSinSub = await prisma.tenant.findMany({
    where: {
      plan: { not: 'trial' },
      id: { notIn: excluded },
      subscriptions: { none: {} },
    },
    select: { id: true, nombre: true, rfc: true, plan: true, createdAt: true },
  });
  for (const t of tenantsConPlanPeroSinSub) {
    if (noRenovacionesMap.has(t.id)) continue;
    noRenovacionesMap.set(t.id, {
      tenantId: t.id,
      tenantNombre: t.nombre,
      rfc: t.rfc ?? '',
      plan: String(t.plan),
      currentPeriodEnd: t.createdAt.toISOString(),
      statusActual: 'sin_suscripcion',
    });
  }

  const noRenovaciones = Array.from(noRenovacionesMap.values());

  // 4) Usuarios por cliente (memberships activos por tenant)
  const memberships = await prisma.tenantMembership.findMany({
    where: { active: true },
    select: { tenantId: true, isOwner: true, tenant: { select: { nombre: true, rfc: true } } },
  });
  const byTenant = new Map<string, { nombre: string; rfc: string; total: number; owners: number }>();
  for (const m of memberships) {
    const ent = byTenant.get(m.tenantId) ?? {
      nombre: m.tenant?.nombre ?? '',
      rfc: m.tenant?.rfc ?? '',
      total: 0,
      owners: 0,
    };
    ent.total += 1;
    if (m.isOwner) ent.owners += 1;
    byTenant.set(m.tenantId, ent);
  }
  const usuariosPorCliente = Array.from(byTenant.entries()).map(([tenantId, v]) => ({
    tenantId,
    tenantNombre: v.nombre,
    rfc: v.rfc,
    activeUsers: v.total,
    owners: v.owners,
  })).sort((a, b) => b.activeUsers - a.activeUsers);

  return { suscripcionesPorPlan, ingresos, noRenovaciones, usuariosPorCliente };
}

/** Lista usuarios activos de un tenant (con email + rol). Para el drill-down de la UI. */
export async function getTenantUsuarios(tenantId: string) {
  const memberships = await prisma.tenantMembership.findMany({
    where: { tenantId, active: true },
    select: {
      isOwner: true,
      joinedAt: true,
      user: { select: { id: true, email: true, nombre: true, active: true, lastLogin: true } },
      rol: { select: { nombre: true } },
    },
    orderBy: { joinedAt: 'asc' },
  });
  return memberships
    .filter(m => m.user.active)
    .map(m => ({
      userId: m.user.id,
      email: m.user.email,
      nombre: m.user.nombre,
      rol: m.rol?.nombre ?? 'sin_rol',
      isOwner: m.isOwner,
      joinedAt: m.joinedAt.toISOString(),
      lastLogin: m.user.lastLogin?.toISOString() ?? null,
    }));
}
