import { Router, Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import bcrypt from 'bcryptjs';
import { UserRole, UserStatus } from '@prisma/client';
import { usersService } from './users.service';
import { createUserSchema, updateUserSchema, listUsersSchema } from './users.schemas';
import { validate } from '../../middleware/validate';
import { authenticate } from '../../middleware/auth.middleware';
import { requireRole } from '../../middleware/rbac.middleware';
import { hasAnyRole, ADMIN_ROLES, getUserRoles } from '../../utils/roles';
import { prisma } from '../../config/database';
import { emailService } from '../../services/email.service';
import { eventBus } from '../../services/event-bus.service';
import { env } from '../../config/env';
import { logger } from '../../utils/logger';
import { userProvisioningService } from '../../services/user-provisioning/provisioning.service';

/** Check if user can manage other users (SUPER_ADMIN or canManageUsers permission) */
function canManageUsers(user: { roles?: unknown; role?: UserRole; permissions?: Record<string, unknown> | null }): boolean {
  const roles = getUserRoles(user);
  if (roles.includes(UserRole.SUPER_ADMIN)) return true;
  return !!(user.permissions as Record<string, unknown> | null)?.canManageUsers;
}

/** Middleware: require user management permission */
function requireUserManagement(req: Request, res: Response, next: NextFunction) {
  if (!req.user) {
    res.status(401).json({ error: { message: 'Authentication required', code: 'AUTH_REQUIRED' } });
    return;
  }
  if (!canManageUsers(req.user as any)) {
    res.status(403).json({ error: { message: 'Insufficient permissions', code: 'FORBIDDEN' } });
    return;
  }
  next();
}

const router = Router();

// All user routes require authentication
router.use(authenticate);

// GET /api/users — list users (any admin)
router.get(
  '/',
  requireRole(...ADMIN_ROLES),
  validate(listUsersSchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await usersService.findAll(req.query as any);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/users/:id — get user (admin or self)
router.get(
  '/:id',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const isAdminUser = hasAnyRole(req.user!, ADMIN_ROLES);
      const isSelf = req.user!.id === id;

      if (!isAdminUser && !isSelf) {
        res.status(403).json({ error: { message: 'Insufficient permissions', code: 'FORBIDDEN' } });
        return;
      }

      const user = await usersService.findById(id);
      res.json(user);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/users — create user (SUPER_ADMIN or canManageUsers)
router.post(
  '/',
  requireUserManagement,
  validate(createUserSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const user = await usersService.create(req.body);
      res.status(201).json(user);
    } catch (err) {
      next(err);
    }
  }
);

// PUT /api/users/:id — update user (admin or self, role changes require user management perm)
router.put(
  '/:id',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const isAdminUser = hasAnyRole(req.user!, ADMIN_ROLES);
      const isSelf = req.user!.id === id;
      const canManage = canManageUsers(req.user as any);

      if (!isAdminUser && !isSelf) {
        res.status(403).json({ error: { message: 'Insufficient permissions', code: 'FORBIDDEN' } });
        return;
      }

      // Only users with management permission can change role, roles, or status
      if (!canManage) {
        delete req.body.role;
        delete req.body.roles;
        delete req.body.status;
        delete req.body.permissions;
      }

      // Self-service password change requires current password verification
      if (isSelf && req.body.password) {
        if (!req.body.currentPassword) {
          res.status(400).json({ error: { message: 'Current password is required to change your password', code: 'CURRENT_PASSWORD_REQUIRED' } });
          return;
        }
        const currentUser = await prisma.user.findUnique({
          where: { id },
          select: { password: true },
        });
        if (!currentUser) {
          res.status(404).json({ error: { message: 'User not found', code: 'NOT_FOUND' } });
          return;
        }
        const valid = await bcrypt.compare(req.body.currentPassword, currentUser.password);
        if (!valid) {
          res.status(401).json({ error: { message: 'Current password is incorrect', code: 'INVALID_CREDENTIALS' } });
          return;
        }
        delete req.body.currentPassword;
      }

      const parsed = updateUserSchema.parse(req.body);
      const user = await usersService.update(id, parsed);
      res.json(user);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/users/:id/approve — approve pending user (SUPER_ADMIN or canManageUsers)
router.post(
  '/:id/approve',
  requireUserManagement,
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const user = await prisma.user.findUnique({ where: { id } });

      if (!user) {
        res.status(404).json({ error: { message: 'User not found', code: 'USER_NOT_FOUND' } });
        return;
      }

      if (user.status !== UserStatus.PENDING_APPROVAL) {
        res.status(400).json({ error: { message: 'User is not pending approval', code: 'INVALID_STATUS' } });
        return;
      }

      await prisma.user.update({
        where: { id },
        data: { status: UserStatus.ACTIVE },
      });

      // Send approval notification email
      const adminUrl = env.ADMIN_URL || 'http://localhost:3000';
      await emailService.sendAccountApprovedEmail({
        recipientEmail: user.email,
        recipientName: user.name || 'there',
        loginUrl: `${adminUrl}/login`,
      }).catch(err => logger.error('Failed to send approval email:', err));

      // Fire-and-forget: provision approved user to eager services
      userProvisioningService.onUserCreated({
        id: user.id, email: user.email, name: user.name, role: user.role,
        roles: user.roles, status: 'ACTIVE', permissions: user.permissions as Record<string, unknown> | null,
      }).catch(err => logger.warn('User provisioning hook (approve) failed:', err));

      eventBus.publish('user.approved', {
        userId: user.id,
        email: user.email,
        name: user.name || '',
        role: user.role,
        approvedByUserId: req.user!.id,
      });

      res.json({ message: 'User approved', userId: id });
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/users/:id/reject — reject pending user (SUPER_ADMIN or canManageUsers)
const rejectSchema = z.object({
  reason: z.string().max(500).optional(),
});

router.post(
  '/:id/reject',
  requireUserManagement,
  validate(rejectSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const user = await prisma.user.findUnique({ where: { id } });

      if (!user) {
        res.status(404).json({ error: { message: 'User not found', code: 'USER_NOT_FOUND' } });
        return;
      }

      if (user.status !== UserStatus.PENDING_APPROVAL) {
        res.status(400).json({ error: { message: 'User is not pending approval', code: 'INVALID_STATUS' } });
        return;
      }

      await prisma.user.update({
        where: { id },
        data: { status: UserStatus.INACTIVE },
      });

      res.json({ message: 'User rejected', userId: id });
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/users/:id/contact — get linked Contact for a user (any admin)
router.get(
  '/:id/contact',
  requireRole(...ADMIN_ROLES),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const contact = await prisma.contact.findFirst({
        where: { userId: id, mergedIntoId: null },
        select: {
          id: true, displayName: true, email: true, phone: true,
          tags: true, supportLevel: true, primarySource: true,
          profileToken: true, coverPhotoPath: true,
        },
      });
      res.json({ contact: contact || null });
    } catch (err) {
      next(err);
    }
  }
);

// DELETE /api/users/:id — delete user (SUPER_ADMIN or canManageUsers)
router.delete(
  '/:id',
  requireUserManagement,
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      await usersService.delete(id);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

export { router as usersRouter };
