/**
 * Role Routes
 *
 * CRUD API for Role-Based Access Control (RBAC) roles.
 * All routes require authentication + super-admin role.
 */

import { Router, Request, Response } from 'express';
import { body, param, query, validationResult } from 'express-validator';
import { getModels } from '../models';
import { authenticate, requireRole } from '../middleware/auth';
import { asyncHandler } from '../middleware/errorHandler';
import { logAudit } from '../utils/auditLogger';
import { invalidatePermissionCache } from '../middleware/permissions';
import { IPermission } from '../models/Role';

export const roleRoutes = Router();

// Apply authentication and super-admin requirement to all routes
roleRoutes.use(authenticate);
roleRoutes.use(requireRole('super-admin'));

// ============================================
// CONSTANTS
// ============================================

const VALID_ACTIONS = [
  'view',
  'create',
  'edit',
  'delete',
  'ai-generate',
  'export',
  'import',
  'download',
  'upload',
  'generate',
  'manage',
  'assign',
  'share',
  'approve',
  'publish',
] as const;

type ValidAction = (typeof VALID_ACTIONS)[number];

/**
 * Default permissions matrix derived from the frontend module registry.
 * Each entry lists the module id, display name, and the actions that are
 * applicable for that module. The frontend uses this to render permission
 * checkboxes when creating or editing a role.
 */
const DEFAULT_PERMISSION_MATRIX = [
  // Foundation
  { module: 'business-profile', name: 'Business Profile', actions: ['view', 'edit', 'ai-generate', 'export'] },
  { module: 'founders', name: 'Founders', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'employees', name: 'Employees', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'products', name: 'Products', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'icp-personas', name: 'ICPs & Personas', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'competitors', name: 'Competitors', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },

  // Brand
  { module: 'brand', name: 'Brand', actions: ['view', 'edit', 'export'] },
  { module: 'brand-strategy', name: 'Brand Strategy', actions: ['view', 'edit', 'ai-generate', 'export'] },
  { module: 'visual-identity', name: 'Visual Identity', actions: ['view', 'edit', 'export'] },
  { module: 'brand-manual', name: 'Brand Manual', actions: ['view', 'download', 'export'] },
  { module: 'brand-assets', name: 'Brand Assets', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'upload', 'download', 'export'] },
  { module: 'stationery', name: 'Stationery', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'hr-assets', name: 'HR Assets', actions: ['view', 'edit', 'download', 'export'] },

  // Content
  { module: 'seo', name: 'SEO', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'blog-content-os', name: 'Blog Content', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'case-studies', name: 'Case Studies', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'testimonials', name: 'Testimonials', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'faq-bank', name: 'FAQ Bank', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'website-planner', name: 'Website Planner', actions: ['view', 'create', 'edit', 'ai-generate', 'export'] },
  { module: 'newsletter-content-os', name: 'Newsletter Content', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'social-media-os', name: 'Social Media', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },

  // Sales
  { module: 'landing-pages', name: 'Landing Pages', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'whatsapp-nurturing', name: 'WhatsApp Nurturing', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'sales-scripts', name: 'Sales Scripts', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'sales-collateral', name: 'Sales Collateral', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'video-content', name: 'Video Content', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'books', name: 'Books', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },

  // Sales Performance
  { module: 'commission-tracker', name: 'Commission Tracker', actions: ['view', 'create', 'edit', 'delete', 'export'] },
  { module: 'sales-targets', name: 'Sales Targets & Quotas', actions: ['view', 'create', 'edit', 'delete', 'export'] },
  { module: 'sales-reports', name: 'Sales Reports & Dashboards', actions: ['view', 'export'] },
  { module: 'sales-playbooks', name: 'Sales Playbooks', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'proposals-quotes', name: 'Proposals & Quotes', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export', 'download'] },

  // Marketing
  { module: 'ads', name: 'Ads Management', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'pr', name: 'PR Content Engine', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'email-templates', name: 'Email Templates', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'intro-scripts', name: 'Intro Scripts', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'courses', name: 'Courses', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'events', name: 'Events', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'guerrilla-marketing', name: 'Guerrilla Marketing', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'interview-media-prep', name: 'Interview & Media Prep', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'speaking-engagements', name: 'Speaking Engagements', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'marketing-calendar', name: 'Marketing Calendar & Timing', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'marketing-channels', name: 'Marketing Channels Map', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'influencer', name: 'Influencer Management', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'gmb', name: 'Google Business Profile', actions: ['view', 'edit', 'ai-generate', 'export'] },

  // Programs
  { module: 'loyalty-programme', name: 'Loyalty Programme', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'membership-plans', name: 'Membership Plans', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'referral-programme', name: 'Referral Programme', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'sops', name: 'SOPs', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },

  // Ops
  { module: 'legal-documents', name: 'Legal Documents', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },
  { module: 'ai-processing', name: 'AI Processing', actions: ['view'] },

  // Publishing
  { module: 'magazine-sponsorship', name: 'Magazine & Sponsorship', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },

  // Funding
  { module: 'funding', name: 'Funding', actions: ['view', 'create', 'edit', 'delete', 'ai-generate', 'export'] },

  // System
  { module: 'ai-chat', name: 'AI Chat', actions: ['view'] },
];

// ============================================
// HELPERS
// ============================================

function isValidAction(action: string): action is ValidAction {
  return VALID_ACTIONS.includes(action as ValidAction);
}

function validatePermissions(permissions: any[]): string | null {
  if (!Array.isArray(permissions)) {
    return 'Permissions must be an array';
  }
  for (const perm of permissions) {
    if (!perm || typeof perm !== 'object') {
      return 'Each permission must be an object';
    }
    if (!perm.module || typeof perm.module !== 'string') {
      return 'Each permission must have a module';
    }
    if (!Array.isArray(perm.actions)) {
      return 'Each permission must have an actions array';
    }
    for (const action of perm.actions) {
      if (!isValidAction(action)) {
        return `Invalid action: "${action}". Valid actions are: ${VALID_ACTIONS.join(', ')}`;
      }
    }
  }
  return null;
}

// ============================================
// GET /default-permissions — Permission matrix for UI
// ============================================

roleRoutes.get('/default-permissions', asyncHandler(async (req: Request, res: Response) => {
  res.json({
    modules: DEFAULT_PERMISSION_MATRIX,
    validActions: VALID_ACTIONS,
  });
}));

// ============================================
// GET / — List all roles
// ============================================

roleRoutes.get(
  '/',
  [
    query('includeInactive').optional().isBoolean(),
  ],
  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 { Role } = getModels();
    const includeInactive = req.query.includeInactive === 'true';

    const filter: any = {};
    if (!includeInactive) {
      filter.isActive = true;
    }

    const roles = await Role.find(filter).sort({ isSystem: -1, isDefault: -1, name: 1 }).lean();

    res.json(roles);
  })
);

// ============================================
// GET /:id — Get role by ID
// ============================================

roleRoutes.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 { Role } = getModels();
    const { id } = req.params;

    const role = await Role.findById(id).lean();
    if (!role) {
      res.status(404).json({ error: 'Role not found' });
      return;
    }

    res.json(role);
  })
);

// ============================================
// POST / — Create new role
// ============================================

roleRoutes.post(
  '/',
  [
    body('name').trim().notEmpty().withMessage('Role name is required')
      .isLength({ max: 50 }).withMessage('Role name cannot exceed 50 characters'),
    body('displayName').trim().notEmpty().withMessage('Display name is required')
      .isLength({ max: 100 }).withMessage('Display name cannot exceed 100 characters'),
    body('description').optional().trim().isLength({ max: 500 }),
    body('permissions').isArray().withMessage('Permissions must be an array'),
    body('isActive').optional().isBoolean(),
  ],
  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 { Role } = getModels();
    const { name, displayName, description, permissions, isActive } = req.body;

    // Validate permission actions
    const permError = validatePermissions(permissions);
    if (permError) {
      res.status(400).json({ error: permError });
      return;
    }

    // Check name uniqueness
    const existing = await Role.findOne({ name: name.toLowerCase() });
    if (existing) {
      res.status(409).json({ error: 'A role with this name already exists' });
      return;
    }

    const role = await Role.create({
      name: name.toLowerCase().trim(),
      displayName: displayName.trim(),
      description: description?.trim() || '',
      permissions,
      isActive: isActive !== undefined ? isActive : true,
      isDefault: false,
      isSystem: false,
    });

    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: 'role.create',
      resource: 'Role',
      resourceId: role._id?.toString(),
      companyId: req.user!.activeCompanyId || undefined,
      details: { name: role.name, displayName: role.displayName, permissionsCount: role.permissions?.length || 0 },
      req,
    });

    // Invalidate permission caches for all users with this role
    try {
      const { User } = getModels();
      const usersWithRole = await User.find({ roleId: role._id }).select('_id').lean();
      for (const u of usersWithRole) {
        await invalidatePermissionCache(u._id.toString());
      }
    } catch { /* non-blocking */ }

    res.status(201).json(role);
  })
);

// ============================================
// PUT /:id — Update role
// ============================================

roleRoutes.put(
  '/:id',
  [
    param('id').notEmpty(),
    body('name').optional().trim().isLength({ min: 1, max: 50 }),
    body('displayName').optional().trim().isLength({ min: 1, max: 100 }),
    body('description').optional().trim().isLength({ max: 500 }),
    body('permissions').optional().isArray(),
    body('isActive').optional().isBoolean(),
  ],
  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 { Role } = getModels();
    const { id } = req.params;
    const { name, displayName, description, permissions, isActive } = req.body;

    const role = await Role.findById(id);
    if (!role) {
      res.status(404).json({ error: 'Role not found' });
      return;
    }

    // Cannot modify system roles
    if ((role as any).isSystem) {
      res.status(403).json({ error: 'Cannot modify system roles' });
      return;
    }

    // Validate permissions if provided
    if (permissions) {
      const permError = validatePermissions(permissions);
      if (permError) {
        res.status(400).json({ error: permError });
        return;
      }
    }

    // Check name uniqueness if renaming
    if (name && name.toLowerCase().trim() !== (role as any).name) {
      const existing = await Role.findOne({ name: name.toLowerCase().trim(), _id: { $ne: id } });
      if (existing) {
        res.status(409).json({ error: 'A role with this name already exists' });
        return;
      }
      (role as any).name = name.toLowerCase().trim();
    }

    if (displayName) (role as any).displayName = displayName.trim();
    if (description !== undefined) (role as any).description = description?.trim() || '';
    if (permissions) (role as any).permissions = permissions;
    if (isActive !== undefined) (role as any).isActive = isActive;

    await (role as any).save();

    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: 'role.update',
      resource: 'Role',
      resourceId: (role as any)._id?.toString(),
      companyId: req.user!.activeCompanyId || undefined,
      details: { name: (role as any).name, displayName: (role as any).displayName },
      req,
    });

    // Invalidate permission caches for all users with this role
    try {
      const { User } = getModels();
      const usersWithRole = await User.find({ roleId: id }).select('_id').lean();
      for (const u of usersWithRole) {
        await invalidatePermissionCache(u._id.toString());
      }
    } catch { /* non-blocking */ }

    res.json(role);
  })
);

// ============================================
// DELETE /:id — Delete role
// ============================================

roleRoutes.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 { Role } = getModels();
    const { id } = req.params;

    const role = await Role.findById(id);
    if (!role) {
      res.status(404).json({ error: 'Role not found' });
      return;
    }

    // Cannot delete system or default roles
    if ((role as any).isSystem) {
      res.status(403).json({ error: 'Cannot delete system roles' });
      return;
    }
    if ((role as any).isDefault) {
      res.status(403).json({ error: 'Cannot delete default roles. Deactivate them instead.' });
      return;
    }

    await Role.findByIdAndDelete(id);

    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: 'role.delete',
      resource: 'Role',
      resourceId: id,
      companyId: req.user!.activeCompanyId || undefined,
      details: { name: (role as any).name },
      req,
    });

    // Invalidate permission caches for all users who had this role
    try {
      const { User } = getModels();
      const usersWithRole = await User.find({ roleId: id }).select('_id').lean();
      for (const u of usersWithRole) {
        await invalidatePermissionCache(u._id.toString());
      }
    } catch { /* non-blocking */ }

    res.json({ message: 'Role deleted successfully' });
  })
);

// ============================================
// PATCH /:id/toggle-status — Activate/deactivate role
// ============================================

roleRoutes.patch(
  '/:id/toggle-status',
  [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 { Role } = getModels();
    const { id } = req.params;

    const role = await Role.findById(id);
    if (!role) {
      res.status(404).json({ error: 'Role not found' });
      return;
    }

    // Cannot deactivate system roles
    if ((role as any).isSystem && (role as any).isActive) {
      res.status(403).json({ error: 'Cannot deactivate system roles' });
      return;
    }

    (role as any).isActive = !(role as any).isActive;
    await (role as any).save();

    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: 'role.toggle',
      resource: 'Role',
      resourceId: id,
      companyId: req.user!.activeCompanyId || undefined,
      details: { name: (role as any).name, isActive: (role as any).isActive },
      req,
    });

    // Invalidate permission caches for all users with this role
    try {
      const { User } = getModels();
      const usersWithRole = await User.find({ roleId: id }).select('_id').lean();
      for (const u of usersWithRole) {
        await invalidatePermissionCache(u._id.toString());
      }
    } catch { /* non-blocking */ }

    res.json({
      id: (role as any)._id || (role as any).id,
      name: (role as any).name,
      displayName: (role as any).displayName,
      isActive: (role as any).isActive,
    });
  })
);

// ============================================
// POST /:id/clone — Clone a role
// ============================================

roleRoutes.post(
  '/:id/clone',
  [
    param('id').notEmpty(),
    body('name').trim().notEmpty().withMessage('Role name is required')
      .isLength({ max: 50 }).withMessage('Role name cannot exceed 50 characters'),
    body('displayName').trim().notEmpty().withMessage('Display name is required')
      .isLength({ max: 100 }).withMessage('Display name cannot exceed 100 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 { Role } = getModels();
    const { id } = req.params;
    const { name, displayName } = req.body;

    // Find the source role
    const sourceRole = await Role.findById(id).lean();
    if (!sourceRole) {
      res.status(404).json({ error: 'Source role not found' });
      return;
    }

    // Check name uniqueness
    const existing = await Role.findOne({ name: name.toLowerCase().trim() });
    if (existing) {
      res.status(409).json({ error: 'A role with this name already exists' });
      return;
    }

    // Create a clone with new name, forcing isDefault=false and isSystem=false
    const clonedRole = await Role.create({
      name: name.toLowerCase().trim(),
      displayName: displayName.trim(),
      description: (sourceRole as any).description || '',
      permissions: (sourceRole as any).permissions || [],
      isActive: true,
      isDefault: false,
      isSystem: false,
    });

    await logAudit({
      userId: req.user!._id.toString(),
      userEmail: req.user!.email,
      action: 'role.clone',
      resource: 'Role',
      resourceId: (clonedRole as any)._id?.toString(),
      companyId: req.user!.activeCompanyId || undefined,
      details: { sourceRoleId: id, newName: name },
      req,
    });

    res.status(201).json(clonedRole);
  })
);

export default roleRoutes;