import { Router } from 'express';
import { authenticate, loadSchoolScope } from '../../middleware/auth';
import { requireRoles } from '../../middleware/rbac';
import { validateBody } from '../../middleware/validate';
import { Role } from '@saferoute/constants';
import { prisma } from '../../config/database';
import { sendSuccess, sendError, sendPaginated } from '../../utils/response';
import { ErrorCode } from '@saferoute/constants';
import { createAuditLog } from '../../utils/audit-logger';
import { z } from 'zod';

export const userRoutes = Router();

const createUserSchema = z.object({
  phone: z.string().min(10).max(15),
  email: z.string().email().optional(),
  fullName: z.string().min(1).max(255),
});

const assignRoleSchema = z.object({
  roleId: z.string().uuid(),
  schoolId: z.string().uuid().optional(),
});

userRoutes.get('/', authenticate, requireRoles(Role.OWNER, Role.CTO, Role.OPERATIONS_MANAGER), async (req, res) => {
  try {
    const page = parseInt(req.query.page as string) || 1;
    const limit = parseInt(req.query.limit as string) || 20;
    const skip = (page - 1) * limit;

    const [users, total] = await Promise.all([
      prisma.user.findMany({
        skip,
        take: limit,
        include: { userRoles: { include: { role: true, school: { select: { id: true, name: true } } } } },
        orderBy: { createdAt: 'desc' },
      }),
      prisma.user.count(),
    ]);

    const items = users.map((u) => ({
      id: u.id,
      phone: u.phone,
      email: u.email,
      fullName: u.fullName,
      isActive: u.isActive,
      roles: u.userRoles.map((ur) => ({ role: ur.role.name, school: ur.school })),
      createdAt: u.createdAt.toISOString(),
    }));

    sendPaginated(res, items, total, page, limit);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to list users.');
  }
});

userRoutes.post('/', authenticate, requireRoles(Role.OWNER, Role.CTO, Role.OPERATIONS_MANAGER), validateBody(createUserSchema), async (req, res) => {
  try {
    const { phone, email, fullName } = req.body;
    const existing = await prisma.user.findUnique({ where: { phone } });
    if (existing) {
      sendError(res, ErrorCode.VALIDATION_ERROR, 'User with this phone already exists.');
      return;
    }

    const user = await prisma.user.create({ data: { phone, email, fullName } });
    await createAuditLog({ actorId: req.user!.userId, action: 'CREATE_USER', entityType: 'user', entityId: user.id });
    sendSuccess(res, { id: user.id, phone: user.phone, fullName: user.fullName }, 'User created successfully', 201);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to create user.');
  }
});

userRoutes.get('/:userId', authenticate, requireRoles(Role.OWNER, Role.CTO, Role.OPERATIONS_MANAGER), async (req, res) => {
  try {
    const user = await prisma.user.findUnique({
      where: { id: req.params.userId },
      include: { userRoles: { include: { role: true, school: { select: { id: true, name: true } } } } },
    });
    if (!user) { sendError(res, ErrorCode.RESOURCE_NOT_FOUND, 'User not found.'); return; }

    sendSuccess(res, {
      id: user.id, phone: user.phone, email: user.email, fullName: user.fullName,
      profilePhotoUrl: user.profilePhotoUrl, isActive: user.isActive,
      roles: user.userRoles.map((ur) => ({ role: ur.role.name, portalType: ur.role.portalType, school: ur.school })),
      createdAt: user.createdAt.toISOString(),
    });
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to get user.');
  }
});

userRoutes.patch('/:userId', authenticate, requireRoles(Role.OWNER, Role.CTO, Role.OPERATIONS_MANAGER), async (req, res) => {
  try {
    const { email, fullName, isActive } = req.body;
    const user = await prisma.user.update({
      where: { id: req.params.userId },
      data: { ...(email !== undefined && { email }), ...(fullName !== undefined && { fullName }), ...(isActive !== undefined && { isActive }) },
    });
    await createAuditLog({ actorId: req.user!.userId, action: 'UPDATE_USER', entityType: 'user', entityId: user.id });
    sendSuccess(res, { id: user.id, fullName: user.fullName, isActive: user.isActive }, 'User updated successfully');
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to update user.');
  }
});

userRoutes.delete('/:userId', authenticate, requireRoles(Role.OWNER), async (req, res) => {
  try {
    await prisma.user.update({ where: { id: req.params.userId }, data: { isActive: false } });
    await createAuditLog({ actorId: req.user!.userId, action: 'DEACTIVATE_USER', entityType: 'user', entityId: req.params.userId });
    sendSuccess(res, null, 'User deactivated successfully');
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to deactivate user.');
  }
});

userRoutes.get('/roles/all', authenticate, requireRoles(Role.OWNER, Role.CTO), async (_req, res) => {
  try {
    const roles = await prisma.roleModel.findMany({ orderBy: { name: 'asc' } });
    sendSuccess(res, roles.map((r) => ({ id: r.id, name: r.name, description: r.description, portalType: r.portalType })));
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to list roles.');
  }
});

userRoutes.post('/:userId/roles', authenticate, requireRoles(Role.OWNER, Role.CTO), validateBody(assignRoleSchema), async (req, res) => {
  try {
    const { roleId, schoolId } = req.body;
    const existing = await prisma.userRole.findFirst({ where: { userId: req.params.userId, roleId } });
    if (existing) { sendError(res, ErrorCode.VALIDATION_ERROR, 'Role already assigned to this user.'); return; }

    const assignment = await prisma.userRole.create({
      data: { userId: req.params.userId, roleId, schoolId, assignedBy: req.user!.userId },
      include: { role: true },
    });
    await createAuditLog({
      actorId: req.user!.userId, action: 'ASSIGN_ROLE', entityType: 'user_role', entityId: assignment.id,
      newValue: { userId: req.params.userId, role: assignment.role.name, schoolId },
    });
    sendSuccess(res, { id: assignment.id, role: assignment.role.name }, 'Role assigned successfully', 201);
  } catch (error) {
    sendError(res, ErrorCode.SERVER_ERROR, 'Failed to assign role.');
  }
});
