/**
 * User Management Routes
 *
 * Enhanced user management API for the admin panel.
 * All routes require authentication + super-admin role.
 */

import { Router, Request, Response } from 'express';
import { body, param, query, validationResult } from 'express-validator';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { getModels } from '../models';
import { authenticate, requireRole, generateToken } from '../middleware/auth';
import { asyncHandler } from '../middleware/errorHandler';
import { logAudit } from '../utils/auditLogger';
import { invalidatePermissionCache } from '../middleware/permissions';

export const userManagementRoutes = Router();

// Apply authentication and super-admin requirement to all routes
userManagementRoutes.use(authenticate);
userManagementRoutes.use(requireRole('super-admin'));

// ============================================
// GET / — List users with pagination, search, role/status filter
// ============================================

userManagementRoutes.get(
  '/',
  [
    query('page').optional().isInt({ min: 1 }),
    query('limit').optional().isInt({ min: 1, max: 100 }),
    query('search').optional().trim(),
    query('role').optional().isIn(['super-admin', 'admin', 'editor', 'viewer']),
    query('status').optional().isIn(['active', 'inactive', 'suspended']),
  ],
  asyncHandler(async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { User } = getModels();
    const page = parseInt(req.query.page as string) || 1;
    const limit = parseInt(req.query.limit as string) || 20;
    const search = (req.query.search as string) || '';
    const role = req.query.role as string | undefined;
    const status = req.query.status as string | undefined;

    const filter: any = {};
    if (search) {
      filter.$or = [
        { name: { $regex: search, $options: 'i' } },
        { email: { $regex: search, $options: 'i' } },
        { username: { $regex: search, $options: 'i' } },
      ];
    }
    if (role) filter.role = role;
    if (status) filter.status = status;

    const skip = (page - 1) * limit;
    const [users, total] = await Promise.all([
      User.find(filter).select('-passwordHash').sort({ createdAt: -1 }).skip(skip).limit(limit).lean(),
      User.countDocuments(filter),
    ]);

    res.json({ users, total, page, totalPages: Math.ceil(total / limit) });
  })
);

// ============================================
// GET /:id — Get user detail by ID
// ============================================

userManagementRoutes.get(
  '/:id',
  [param('id').notEmpty()],
  asyncHandler(async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { User } = getModels();
    const { id } = req.params;

    const user = await User.findById(id).select('-passwordHash').lean();
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }

    res.json(user);
  })
);

// ============================================
// POST / — Create user
// ============================================

userManagementRoutes.post(
  '/',
  [
    body('email').isEmail().withMessage('Valid email is required').normalizeEmail(),
    body('name').trim().notEmpty().withMessage('Name is required')
      .isLength({ max: 100 }).withMessage('Name cannot exceed 100 characters'),
    body('password').isLength({ min: 6 }).withMessage('Password must be at least 6 characters'),
    body('role').optional().isIn(['admin', 'editor', 'viewer']),
    body('username').optional().trim().isLength({ min: 3, max: 30 }),
    body('phone').optional().trim(),
    body('companyId').optional().trim(),
  ],
  asyncHandler(async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { User, Company } = getModels();
    const { email, name, password, role, username, phone, companyId } = req.body;

    // Check email uniqueness
    const existingUser = await User.findOne({ email: email.toLowerCase() });
    if (existingUser) {
      res.status(409).json({ error: 'A user with this email already exists' });
      return;
    }

    // Check username uniqueness if provided
    if (username) {
      const existingUsername = await User.findOne({ username });
      if (existingUsername) {
        res.status(409).json({ error: 'A user with this username already exists' });
        return;
      }
    }

    // Hash password
    const salt = await bcrypt.genSalt(12);
    const passwordHash = await bcrypt.hash(password, salt);

    // Create default company if none provided
    let userCompanyIds: string[] = [];
    let activeCompanyId: string | null = null;
    if (companyId) {
      userCompanyIds = [companyId];
      activeCompanyId = companyId;
    } else {
      // Create a default company for the new user
      const company = await Company.create({
        name: `${name}'s Company`,
        ownerId: null,
        settings: {},
      });
      userCompanyIds = [(company as any)._id.toString()];
      activeCompanyId = (company as any)._id.toString();
    }

    const user = await User.create({
      email: email.toLowerCase(),
      name: name.trim(),
      passwordHash,
      role: role || 'viewer',
      username: username || undefined,
      phone: phone || undefined,
      status: 'active',
      companyIds: userCompanyIds,
      activeCompanyId,
      mustChangePassword: true,
    });

    // Return user without password hash
    const userObj = (user as any).toObject ? (user as any).toObject() : user;
    const { passwordHash: _, ...userWithoutPassword } = userObj;

    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: 'user.create',
      resource: 'User',
      resourceId: (user as any)._id?.toString(),
      companyId: req.user!.activeCompanyId || undefined,
      details: { email: userObj.email, name: userObj.name, role: userObj.role },
      req,
    });

    res.status(201).json(userWithoutPassword);
  })
);

// ============================================
// PUT /:id — Update user
// ============================================

userManagementRoutes.put(
  '/:id',
  [
    param('id').notEmpty(),
    body('name').optional().trim().isLength({ min: 1, max: 100 }),
    body('email').optional().isEmail().normalizeEmail(),
    body('username').optional().trim().isLength({ min: 3, max: 30 }),
    body('phone').optional().trim(),
    body('status').optional().isIn(['active', 'inactive', 'suspended']),
    body('roleId').optional().trim(),
    body('notes').optional().trim().isLength({ max: 1000 }),
    body('profileImage').optional().trim(),
  ],
  asyncHandler(async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { User } = getModels();
    const { id } = req.params;
    const { name, email, username, phone, status, roleId, notes, profileImage } = req.body;

    const user = await User.findById(id);
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }

    // Cannot modify super-admin
    if (user.role === 'super-admin') {
      res.status(403).json({ error: 'Cannot modify Super Admin' });
      return;
    }

    // Check email uniqueness if changing
    if (email && email.toLowerCase() !== user.email) {
      const existingUser = await User.findOne({ email: email.toLowerCase(), _id: { $ne: id } });
      if (existingUser) {
        res.status(409).json({ error: 'A user with this email already exists' });
        return;
      }
      user.email = email.toLowerCase();
    }

    // Check username uniqueness if changing
    if (username && username !== (user as any).username) {
      const existingUsername = await User.findOne({ username, _id: { $ne: id } });
      if (existingUsername) {
        res.status(409).json({ error: 'A user with this username already exists' });
        return;
      }
      (user as any).username = username;
    }

    if (name) user.name = name.trim();
    if (phone !== undefined) (user as any).phone = phone;
    if (status) user.status = status;
    if (roleId !== undefined) (user as any).roleId = roleId;
    if (notes !== undefined) (user as any).notes = notes;
    if (profileImage !== undefined) (user as any).profileImage = profileImage;

    await user.save();

    // Audit log
    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: 'user.update',
      resource: 'User',
      resourceId: id,
      companyId: req.user!.activeCompanyId || undefined,
      details: { name: user.name, email: user.email, changedFields: Object.keys(req.body).filter(k => k !== 'password') },
      req,
    });

    // Invalidate permission cache if role or status changed
    if (req.body.roleId || req.body.status) {
      await invalidatePermissionCache(id);
    }

    // Return without password hash
    const userObj = (user as any).toObject ? (user as any).toObject() : { ...user };
    const { passwordHash: _, ...userWithoutPassword } = userObj;
    res.json(userWithoutPassword);
  })
);

// ============================================
// PATCH /:id/status — Update user status
// ============================================

userManagementRoutes.patch(
  '/:id/status',
  [
    param('id').notEmpty(),
    body('status').isIn(['active', 'inactive', 'suspended']).withMessage('Status must be active, inactive, or suspended'),
  ],
  asyncHandler(async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { User } = getModels();
    const { id } = req.params;
    const { status } = req.body;

    const user = await User.findById(id);
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }

    // Cannot deactivate super-admin
    if (user.role === 'super-admin' && status !== 'active') {
      res.status(403).json({ error: 'Cannot deactivate or suspend Super Admin' });
      return;
    }

    user.status = status;
    await user.save();

    // Audit log
    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: 'user.status-change',
      resource: 'User',
      resourceId: id,
      companyId: req.user!.activeCompanyId || undefined,
      details: { name: user.name, email: user.email, newStatus: status },
      req,
    });

    // Invalidate permission cache — status change may affect access
    await invalidatePermissionCache(id);

    res.json({
      id: (user as any)._id || (user as any).id,
      name: user.name,
      email: user.email,
      status: user.status,
    });
  })
);

// ============================================
// DELETE /:id — Delete user
// ============================================

userManagementRoutes.delete(
  '/:id',
  [param('id').notEmpty()],
  asyncHandler(async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { User } = getModels();
    const { id } = req.params;

    // Cannot delete yourself
    if (id === req.user!._id!.toString()) {
      res.status(400).json({ error: 'Cannot delete yourself' });
      return;
    }

    const user = await User.findById(id);
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }

    // Cannot delete super-admin
    if (user.role === 'super-admin') {
      res.status(403).json({ error: 'Cannot delete Super Admin' });
      return;
    }

    await User.findByIdAndDelete(id);

    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: 'user.delete',
      resource: 'User',
      resourceId: id,
      companyId: req.user!.activeCompanyId || undefined,
      details: { email: (user as any).email, name: (user as any).name },
      req,
    });

    res.json({ message: 'User deleted successfully' });
  })
);

// ============================================
// PATCH /:id/reset-password — Force password reset
// ============================================

userManagementRoutes.patch(
  '/:id/reset-password',
  [
    param('id').notEmpty(),
    body('newPassword').isLength({ min: 6 }).withMessage('New password must be at least 6 characters'),
  ],
  asyncHandler(async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { User } = getModels();
    const { id } = req.params;
    const { newPassword } = req.body;

    const user = await User.findById(id);
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }

    // Hash the new password
    const salt = await bcrypt.genSalt(12);
    user.passwordHash = await bcrypt.hash(newPassword, salt);
    (user as any).mustChangePassword = true;

    await user.save();

    // Audit log
    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: 'user.reset-password',
      resource: 'User',
      resourceId: id,
      companyId: req.user!.activeCompanyId || undefined,
      details: { targetEmail: user.email, targetName: user.name },
      req,
    });

    res.json({ message: 'Password reset successfully. User must change password on next login.' });
  })
);

// ============================================
// PATCH /:id/lock — Lock/unlock user
// ============================================

userManagementRoutes.patch(
  '/:id/lock',
  [
    param('id').notEmpty(),
    body('isLocked').isBoolean().withMessage('isLocked must be a boolean'),
  ],
  asyncHandler(async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { User } = getModels();
    const { id } = req.params;
    const { isLocked } = req.body;

    const user = await User.findById(id);
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }

    // Cannot lock super-admin
    if (user.role === 'super-admin' && isLocked) {
      res.status(403).json({ error: 'Cannot lock Super Admin' });
      return;
    }

    (user as any).isLocked = isLocked;
    await user.save();

    // Audit log
    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: isLocked ? 'user.lock' : 'user.unlock',
      resource: 'User',
      resourceId: id,
      companyId: req.user!.activeCompanyId || undefined,
      details: { name: user.name, email: user.email, isLocked },
      req,
    });

    res.json({
      id: (user as any)._id || (user as any).id,
      name: user.name,
      email: user.email,
      isLocked: (user as any).isLocked,
    });
  })
);

// ============================================
// GET /:id/permissions — Resolve effective permissions for a user
// ============================================

userManagementRoutes.get(
  '/:id/permissions',
  [param('id').notEmpty()],
  asyncHandler(async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { User, Role, UserAccessOverride } = getModels();
    const { id } = req.params;

    const user = await User.findById(id).lean();
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }

    // Super-admin has all permissions
    if (user.role === 'super-admin') {
      const allModules = [
        'business-profile', 'founders', 'employees', 'products', 'product-categories',
        'icp-personas', 'competitors', 'brand', 'brand-strategy', 'visual-identity',
        'brand-manual', 'brand-assets', 'stationery', 'hr-assets', 'seo',
        'blog-content-os', 'case-studies', 'testimonials', 'faq-bank',
        'website-planner', 'newsletter-content-os', 'social-media-os',
        'landing-pages', 'whatsapp-nurturing', 'sales-scripts', 'sales-collateral',
        'video-content', 'books',
        'commission-tracker', 'sales-targets', 'sales-reports', 'sales-playbooks', 'proposals-quotes',
        'ads', 'pr', 'email-templates', 'intro-scripts',
        'courses', 'events', 'guerrilla-marketing', 'interview-media-prep',
        'speaking-engagements', 'marketing-calendar', 'marketing-channels',
        'influencer', 'gmb', 'loyalty-programme', 'membership-plans',
        'referral-programme', 'sops', 'legal-documents', 'ai-processing',
        'magazine-sponsorship', 'funding', 'ai-chat',
      ];
      const allActions = ['view', 'create', 'edit', 'delete', 'ai-generate', 'export', 'manage'];
      const superAdminPermissions = allModules.map(module => ({
        module,
        actions: [...allActions],
      }));
      res.json({
        userId: (user as any)._id || (user as any).id,
        role: user.role,
        roleId: (user as any).roleId || null,
        rolePermissions: superAdminPermissions,
        overrides: null,
        resolvedPermissions: superAdminPermissions,
        isSuperAdmin: true,
      });
      return;
    }

    // Get role
    let role = null;
    if ((user as any).roleId) {
      role = await Role.findById((user as any).roleId).lean();
    }
    if (!role) {
      role = await Role.findOne({ name: user.role, isDefault: true }).lean();
    }

    const rolePermissions = role ? (role as any).permissions || [] : [];

    // Get user-specific overrides
    const companyId = (user as any).activeCompanyId || (user as any).companyIds?.[0] || '';
    const override = await UserAccessOverride.findOne({ userId: id, companyId }).lean();

    const grants = override ? (override as any).grants || [] : [];
    const denies = override ? (override as any).denies || [] : [];

    // Resolve effective permissions: start with role, apply grants, then denies
    const permissionMap = new Map<string, Set<string>>();

    // Start with role permissions
    for (const perm of rolePermissions) {
      const key = perm.module + (perm.page ? `:${perm.page}` : '') + (perm.feature ? `:${perm.feature}` : '');
      if (!permissionMap.has(key)) {
        permissionMap.set(key, new Set());
      }
      for (const action of perm.actions) {
        permissionMap.get(key)!.add(action);
      }
    }

    // Apply grants (add)
    for (const grant of grants) {
      const key = grant.module + (grant.page ? `:${grant.page}` : '') + (grant.feature ? `:${grant.feature}` : '');
      if (!permissionMap.has(key)) {
        permissionMap.set(key, new Set());
      }
      for (const action of grant.actions) {
        permissionMap.get(key)!.add(action);
      }
    }

    // Apply denies (remove)
    for (const deny of denies) {
      const key = deny.module + (deny.page ? `:${deny.page}` : '') + (deny.feature ? `:${deny.feature}` : '');
      const denyActions: string[] = deny.actions.map((a: string) => a.startsWith('!') ? a.slice(1) : a);
      if (permissionMap.has(key)) {
        const actions = permissionMap.get(key)!;
        for (const action of denyActions) {
          actions.delete(action);
        }
        // Remove entry if no actions remain
        if (actions.size === 0) {
          permissionMap.delete(key);
        }
      }
    }

    // Convert to resolved permissions array
    const resolvedPermissions = Array.from(permissionMap.entries()).map(([key, actions]) => {
      const parts = key.split(':');
      const module = parts[0];
      const page = parts.length > 1 ? parts[1] : undefined;
      const feature = parts.length > 2 ? parts[2] : undefined;
      return {
        module,
        ...(page ? { page } : {}),
        ...(feature ? { feature } : {}),
        actions: Array.from(actions),
      };
    });

    res.json({
      userId: (user as any)._id || (user as any).id,
      role: user.role,
      roleId: (user as any).roleId || null,
      rolePermissions,
      overrides: override ? { grants, denies } : null,
      resolvedPermissions,
      isSuperAdmin: false,
    });
  })
);

// ============================================
// POST /:id/view-as — Generate a temporary JWT token for "view as" functionality
// ============================================

userManagementRoutes.post(
  '/:id/view-as',
  [param('id').notEmpty()],
  asyncHandler(async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { User } = getModels();
    const { id } = req.params;

    const user = await User.findById(id).select('-passwordHash').lean();
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }

    // Cannot view-as a super-admin
    if (user.role === 'super-admin') {
      res.status(403).json({ error: 'Cannot view as Super Admin' });
      return;
    }

    // Cannot view-as a suspended or inactive user
    if (user.status === 'suspended' || user.status === 'inactive') {
      res.status(403).json({ error: 'Cannot view as a suspended or inactive user' });
      return;
    }

    // Generate a temporary JWT token scoped to this user's permissions
    // Token expires in 1 hour
    const JWT_SECRET = process.env.JWT_SECRET || 'dev-only-insecure-jwt-secret-do-not-use-in-production';
    const token = jwt.sign(
      {
        userId: (user as any)._id || (user as any).id,
        viewAs: true,
        originalUserId: req.user!._id!.toString(),
      },
      JWT_SECRET,
      { expiresIn: '1h' }
    );

    res.json({
      token,
      user: {
        id: (user as any)._id || (user as any).id,
        name: user.name,
        role: user.role,
      },
    });

    // Audit log
    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: 'user.view-as',
      resource: 'User',
      resourceId: id,
      companyId: req.user!.activeCompanyId || undefined,
      details: { targetUserName: user.name, targetUserRole: user.role },
      req,
    });
  })
);

export default userManagementRoutes;