import type { Request, Response, NextFunction } from 'express';
import { prisma } from '../config/database.js';
import { hasPlatformRole, invalidatePlatformRolesCache, type PlatformRole } from '../utils/platform-admin.js';
import { auditFromReq } from '../utils/audit.js';

const VALID_ROLES: PlatformRole[] = ['platform_admin', 'platform_ti', 'platform_support', 'platform_sales', 'platform_finance'];
const SUPERSET_ROLES: PlatformRole[] = ['platform_admin', 'platform_ti'];

async function requirePlatformAdmin(req: Request, res: Response): Promise<boolean> {
  const ok = await hasPlatformRole(req.user?.userId, 'platform_admin');
  if (!ok) {
    res.status(403).json({ message: 'Solo platform_admin puede gestionar staff' });
  }
  return ok;
}

/**
 * Lista users que tienen al menos un platform role + users candidatos a serlo.
 * Admin global (platform_admin) only.
 */
export async function listStaff(req: Request, res: Response, next: NextFunction) {
  try {
    if (!(await requirePlatformAdmin(req, res))) return;

    // Todos los users con al menos un platform role
    const roles = await prisma.userPlatformRole.findMany({
      include: {
        user: {
          select: {
            id: true, email: true, nombre: true, active: true,
            // Tenant principal del staff: el primer membership owner por joinedAt
            // ASC. Se incluye solo para mostrar contexto en la UI admin.
            memberships: {
              where: { active: true, isOwner: true },
              orderBy: { joinedAt: 'asc' },
              take: 1,
              include: { tenant: { select: { id: true, nombre: true, rfc: true } } },
            },
          },
        },
      },
      orderBy: { createdAt: 'desc' },
    });

    // Agrupa por user
    const byUser = new Map<string, any>();
    for (const r of roles) {
      const existing = byUser.get(r.userId);
      if (existing) {
        existing.roles.push(r.role);
      } else {
        const { memberships, ...userBase } = r.user;
        byUser.set(r.userId, {
          ...userBase,
          tenant: memberships[0]?.tenant ?? null,
          roles: [r.role],
        });
      }
    }
    res.json(Array.from(byUser.values()));
  } catch (error) {
    next(error);
  }
}

/**
 * Busca users por email (para agregar nuevos staff). Admin global only.
 */
export async function searchUsers(req: Request, res: Response, next: NextFunction) {
  try {
    if (!(await requirePlatformAdmin(req, res))) return;

    const q = String(req.query.q || '').trim();
    if (q.length < 2) return res.json([]);

    const users = await prisma.user.findMany({
      where: {
        OR: [
          { email: { contains: q, mode: 'insensitive' } },
          { nombre: { contains: q, mode: 'insensitive' } },
        ],
      },
      select: {
        id: true, email: true, nombre: true, active: true,
        memberships: {
          where: { active: true, isOwner: true },
          orderBy: { joinedAt: 'asc' },
          take: 1,
          include: { tenant: { select: { id: true, nombre: true, rfc: true } } },
        },
      },
      take: 10,
    });
    res.json(users.map(u => {
      const { memberships, ...rest } = u;
      return { ...rest, tenant: memberships[0]?.tenant ?? null };
    }));
  } catch (error) {
    next(error);
  }
}

/**
 * Asigna un rol a un user. Idempotente (si ya existe, no duplica).
 */
export async function grantRole(req: Request, res: Response, next: NextFunction) {
  try {
    if (!(await requirePlatformAdmin(req, res))) return;

    const { userId, role } = req.body;
    if (!userId || typeof userId !== 'string') {
      return res.status(400).json({ message: 'userId requerido' });
    }
    if (!VALID_ROLES.includes(role)) {
      return res.status(400).json({ message: `role inválido. Valores: ${VALID_ROLES.join(', ')}` });
    }

    const user = await prisma.user.findUnique({ where: { id: userId }, select: { id: true, email: true } });
    if (!user) return res.status(404).json({ message: 'Usuario no encontrado' });

    await prisma.userPlatformRole.upsert({
      where: { userId_role: { userId, role } },
      create: { userId, role, createdBy: req.user!.userId },
      update: {},
    });

    invalidatePlatformRolesCache(userId);

    auditFromReq(req, 'platform_role.granted', {
      entityType: 'User',
      entityId: userId,
      metadata: { role, targetEmail: user.email },
    });

    res.json({ ok: true });
  } catch (error) {
    next(error);
  }
}

/**
 * Quita un rol a un user. Protección: no puedes quitarte tu propio `platform_admin`
 * si eres el último admin (evita bootstrap problem — nadie queda con acceso).
 */
export async function revokeRole(req: Request, res: Response, next: NextFunction) {
  try {
    if (!(await requirePlatformAdmin(req, res))) return;

    const { userId, role } = req.body;
    if (!userId || typeof userId !== 'string') {
      return res.status(400).json({ message: 'userId requerido' });
    }
    if (!VALID_ROLES.includes(role)) {
      return res.status(400).json({ message: 'role inválido' });
    }

    // Protección: no quitar tu último rol superset (admin o TI) — evita bootstrap problem
    if (SUPERSET_ROLES.includes(role) && userId === req.user!.userId) {
      const supersetCount = await prisma.userPlatformRole.count({
        where: { role: { in: SUPERSET_ROLES } },
      });
      if (supersetCount <= 1) {
        return res.status(400).json({
          message: 'No puedes quitar tu propio rol superset — serías el último con acceso transversal. Asigna platform_admin o platform_ti a otro usuario primero.',
        });
      }
    }

    const user = await prisma.user.findUnique({ where: { id: userId }, select: { email: true } });

    await prisma.userPlatformRole.deleteMany({
      where: { userId, role },
    });

    invalidatePlatformRolesCache(userId);

    auditFromReq(req, 'platform_role.revoked', {
      entityType: 'User',
      entityId: userId,
      metadata: { role, targetEmail: user?.email },
    });

    res.json({ ok: true });
  } catch (error) {
    next(error);
  }
}
