const express = require('express');
const router = express.Router();
const { body, validationResult } = require('express-validator');
const { authenticate, requireAdmin } = require('../middleware/auth');
const { requirePermission, requireAllPermissions, PERMISSIONS, DEFAULT_ROLES, clearAllPermissionCache, clearUserPermissionCache, logPermissionAction, getUserPermissions } = require('../middleware/rbac');
const { modifyLimiter, readLimiter } = require('../middleware/rateLimiter');
const { db } = require('../database/db');
const logger = require('../utils/logger');
const SecurityAuditLogger = require('../utils/securityAudit');

/**
 * Get all available permissions
 * Returns the complete permission catalog
 */
router.get('/permissions', authenticate, requirePermission('users.manage_roles'), readLimiter, (req, res) => {
  res.json({
    permissions: Object.entries(PERMISSIONS).map(([key, description]) => ({
      key,
      description
    })),
    categories: {
      'User Management': Object.keys(PERMISSIONS).filter(k => k.startsWith('users.')),
      'Session Management': Object.keys(PERMISSIONS).filter(k => k.startsWith('sessions.')),
      'Content Management': Object.keys(PERMISSIONS).filter(k => k.startsWith('playlists.') || k.startsWith('channels.') || k.startsWith('favorites.') || k.startsWith('history.')),
      'System & Settings': Object.keys(PERMISSIONS).filter(k => k.startsWith('settings.') || k.startsWith('stats.') || k.startsWith('backup.')),
      'Security Management': Object.keys(PERMISSIONS).filter(k => k.startsWith('security.')),
      'Search & Discovery': Object.keys(PERMISSIONS).filter(k => k.startsWith('search.')),
      'VPN & Network': Object.keys(PERMISSIONS).filter(k => k.startsWith('vpn.'))
    }
  });
});

/**
 * Get all roles
 */
router.get('/roles', authenticate, requirePermission('users.view'), readLimiter, (req, res) => {
  db.all(
    `SELECT id, role_key, name, description, permissions, is_system_role, created_at, updated_at
     FROM roles
     ORDER BY is_system_role DESC, name ASC`,
    [],
    (err, roles) => {
      if (err) {
        logger.error('Error fetching roles:', err);
        return res.status(500).json({ error: 'Failed to fetch roles' });
      }

      // Parse permissions JSON
      const rolesWithParsedPermissions = roles.map(role => ({
        ...role,
        permissions: JSON.parse(role.permissions || '[]'),
        is_system_role: Boolean(role.is_system_role)
      }));

      res.json(rolesWithParsedPermissions);
    }
  );
});

/**
 * Get single role by key
 */
router.get('/roles/:roleKey', authenticate, requirePermission('users.view'), readLimiter, (req, res) => {
  const { roleKey } = req.params;

  db.get(
    `SELECT id, role_key, name, description, permissions, is_system_role, created_at, updated_at
     FROM roles WHERE role_key = ?`,
    [roleKey],
    (err, role) => {
      if (err) {
        logger.error('Error fetching role:', err);
        return res.status(500).json({ error: 'Failed to fetch role' });
      }

      if (!role) {
        return res.status(404).json({ error: 'Role not found' });
      }

      res.json({
        ...role,
        permissions: JSON.parse(role.permissions || '[]'),
        is_system_role: Boolean(role.is_system_role)
      });
    }
  );
});

/**
 * Create custom role
 * Only admins with users.manage_roles permission
 */
router.post('/roles',
  authenticate,
  requireAllPermissions(['users.manage_roles', 'users.create']),
  modifyLimiter,
  [
    body('role_key').trim().isLength({ min: 2, max: 50 }).matches(/^[a-z_]+$/).withMessage('Role key must be lowercase with underscores only'),
    body('name').trim().isLength({ min: 2, max: 100 }),
    body('description').optional().trim().isLength({ max: 500 }),
    body('permissions').isArray().withMessage('Permissions must be an array'),
    body('permissions.*').isString().isIn(Object.keys(PERMISSIONS)).withMessage('Invalid permission')
  ],
  async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { role_key, name, description, permissions } = req.body;

    try {
      // Check if role key already exists
      db.get('SELECT id FROM roles WHERE role_key = ?', [role_key], (err, existing) => {
        if (err) {
          logger.error('Error checking role existence:', err);
          return res.status(500).json({ error: 'Failed to create role' });
        }

        if (existing) {
          return res.status(409).json({ error: 'Role key already exists' });
        }

        // Create new role
        db.run(
          `INSERT INTO roles (role_key, name, description, permissions, is_system_role)
           VALUES (?, ?, ?, ?, 0)`,
          [role_key, name, description || '', JSON.stringify(permissions)],
          function(err) {
            if (err) {
              logger.error('Error creating role:', err);
              return res.status(500).json({ error: 'Failed to create role' });
            }

            // Log action
            logPermissionAction(
              req.user.userId,
              'role_created',
              'role',
              this.lastID,
              null,
              { role_key, name, permissions },
              req
            );

            logger.info(`Role created: ${role_key} by user ${req.user.userId}`);

            // Fetch and return the created role
            db.get(
              'SELECT id, role_key, name, description, permissions, is_system_role, created_at FROM roles WHERE id = ?',
              [this.lastID],
              (err, role) => {
                if (err) {
                  return res.status(500).json({ error: 'Role created but failed to fetch details' });
                }
                res.status(201).json({
                  ...role,
                  permissions: JSON.parse(role.permissions),
                  is_system_role: Boolean(role.is_system_role)
                });
              }
            );
          }
        );
      });
    } catch (error) {
      logger.error('Role creation error:', error);
      res.status(500).json({ error: 'Failed to create role' });
    }
  }
);

/**
 * Update role permissions
 * Cannot modify system roles
 */
router.patch('/roles/:roleKey',
  authenticate,
  requirePermission('users.manage_roles'),
  modifyLimiter,
  [
    body('name').optional().trim().isLength({ min: 2, max: 100 }),
    body('description').optional().trim().isLength({ max: 500 }),
    body('permissions').optional().isArray(),
    body('permissions.*').optional().isString().isIn(Object.keys(PERMISSIONS))
  ],
  async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { roleKey } = req.params;
    const { name, description, permissions } = req.body;

    try {
      // Check if role exists and is not a system role
      db.get('SELECT * FROM roles WHERE role_key = ?', [roleKey], (err, role) => {
        if (err) {
          logger.error('Error fetching role:', err);
          return res.status(500).json({ error: 'Failed to update role' });
        }

        if (!role) {
          return res.status(404).json({ error: 'Role not found' });
        }

        if (role.is_system_role) {
          return res.status(403).json({ error: 'Cannot modify system roles' });
        }

        // Build update query
        const updates = [];
        const params = [];

        if (name !== undefined) {
          updates.push('name = ?');
          params.push(name);
        }
        if (description !== undefined) {
          updates.push('description = ?');
          params.push(description);
        }
        if (permissions !== undefined) {
          updates.push('permissions = ?');
          params.push(JSON.stringify(permissions));
        }

        if (updates.length === 0) {
          return res.status(400).json({ error: 'No fields to update' });
        }

        updates.push('updated_at = CURRENT_TIMESTAMP');
        params.push(roleKey);

        // Update role
        db.run(
          `UPDATE roles SET ${updates.join(', ')} WHERE role_key = ?`,
          params,
          function(err) {
            if (err) {
              logger.error('Error updating role:', err);
              return res.status(500).json({ error: 'Failed to update role' });
            }

            if (this.changes === 0) {
              return res.status(404).json({ error: 'Role not found' });
            }

            // Log action
            logPermissionAction(
              req.user.userId,
              'role_updated',
              'role',
              role.id,
              { name: role.name, description: role.description, permissions: JSON.parse(role.permissions) },
              { name, description, permissions },
              req
            );

            // Clear permission cache as role permissions changed
            clearAllPermissionCache();

            logger.info(`Role updated: ${roleKey} by user ${req.user.userId}`);

            // Fetch and return updated role
            db.get(
              'SELECT id, role_key, name, description, permissions, is_system_role, updated_at FROM roles WHERE role_key = ?',
              [roleKey],
              (err, updatedRole) => {
                if (err) {
                  return res.status(500).json({ error: 'Role updated but failed to fetch details' });
                }
                res.json({
                  ...updatedRole,
                  permissions: JSON.parse(updatedRole.permissions),
                  is_system_role: Boolean(updatedRole.is_system_role)
                });
              }
            );
          }
        );
      });
    } catch (error) {
      logger.error('Role update error:', error);
      res.status(500).json({ error: 'Failed to update role' });
    }
  }
);

/**
 * Delete custom role
 * Cannot delete system roles or roles assigned to users
 */
router.delete('/roles/:roleKey',
  authenticate,
  requirePermission('users.manage_roles'),
  modifyLimiter,
  async (req, res) => {
    const { roleKey } = req.params;

    try {
      // Check if role exists
      db.get('SELECT * FROM roles WHERE role_key = ?', [roleKey], (err, role) => {
        if (err) {
          logger.error('Error fetching role:', err);
          return res.status(500).json({ error: 'Failed to delete role' });
        }

        if (!role) {
          return res.status(404).json({ error: 'Role not found' });
        }

        if (role.is_system_role) {
          return res.status(403).json({ error: 'Cannot delete system roles' });
        }

        // Check if role is assigned to any users
        db.get('SELECT COUNT(*) as count FROM users WHERE role = ?', [roleKey], (err, result) => {
          if (err) {
            logger.error('Error checking role usage:', err);
            return res.status(500).json({ error: 'Failed to delete role' });
          }

          if (result.count > 0) {
            return res.status(409).json({ 
              error: 'Cannot delete role that is assigned to users',
              users_count: result.count
            });
          }

          // Delete role
          db.run('DELETE FROM roles WHERE role_key = ?', [roleKey], function(err) {
            if (err) {
              logger.error('Error deleting role:', err);
              return res.status(500).json({ error: 'Failed to delete role' });
            }

            // Log action
            logPermissionAction(
              req.user.userId,
              'role_deleted',
              'role',
              role.id,
              { role_key: roleKey, name: role.name },
              null,
              req
            );

            logger.info(`Role deleted: ${roleKey} by user ${req.user.userId}`);

            res.json({ message: 'Role deleted successfully' });
          });
        });
      });
    } catch (error) {
      logger.error('Role deletion error:', error);
      res.status(500).json({ error: 'Failed to delete role' });
    }
  }
);

/**
 * Get user's current permissions
 */
router.get('/my-permissions', authenticate, readLimiter, async (req, res) => {
  try {
    const permissions = await getUserPermissions(req.user.userId);
    
    // Get role info
    db.get(
      'SELECT u.role, r.name as role_name, r.description as role_description FROM users u LEFT JOIN roles r ON u.role = r.role_key WHERE u.id = ?',
      [req.user.userId],
      (err, roleInfo) => {
        if (err) {
          logger.error('Error fetching role info:', err);
          return res.status(500).json({ error: 'Failed to fetch permissions' });
        }

        res.json({
          role: roleInfo?.role || 'unknown',
          role_name: roleInfo?.role_name || 'Unknown',
          role_description: roleInfo?.role_description || '',
          permissions,
          permission_details: permissions.map(p => ({
            key: p,
            description: PERMISSIONS[p] || 'Unknown permission'
          }))
        });
      }
    );
  } catch (error) {
    logger.error('Error fetching user permissions:', error);
    res.status(500).json({ error: 'Failed to fetch permissions' });
  }
});

/**
 * Assign role to user
 * Requires users.manage_roles permission
 */
router.post('/users/:userId/role',
  authenticate,
  requirePermission('users.manage_roles'),
  modifyLimiter,
  [
    body('role').trim().notEmpty().withMessage('Role is required')
  ],
  async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { userId } = req.params;
    const { role } = req.body;

    try {
      // Check if role exists
      db.get('SELECT role_key FROM roles WHERE role_key = ?', [role], (err, roleExists) => {
        if (err) {
          logger.error('Error checking role:', err);
          return res.status(500).json({ error: 'Failed to assign role' });
        }

        if (!roleExists) {
          return res.status(404).json({ error: 'Role not found' });
        }

        // Check if user exists
        db.get('SELECT id, username, role FROM users WHERE id = ?', [userId], (err, user) => {
          if (err) {
            logger.error('Error fetching user:', err);
            return res.status(500).json({ error: 'Failed to assign role' });
          }

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

          // Prevent modifying own role
          if (parseInt(userId) === req.user.userId) {
            return res.status(403).json({ error: 'Cannot modify your own role' });
          }

          const oldRole = user.role;

          // Update user role
          db.run(
            'UPDATE users SET role = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
            [role, userId],
            async function(err) {
              if (err) {
                logger.error('Error updating user role:', err);
                return res.status(500).json({ error: 'Failed to assign role' });
              }

              // Clear user's permission cache
              clearUserPermissionCache(parseInt(userId));

              const ip = req.ip || req.headers['x-forwarded-for'] || req.connection.remoteAddress;
              const userAgent = req.headers['user-agent'];

              // CWE-778: Log comprehensive privilege change
              await SecurityAuditLogger.logPrivilegeChange(parseInt(userId), 'role_change', {
                ip,
                userAgent,
                previousRole: oldRole,
                newRole: role,
                changedBy: req.user.userId,
                changedByUsername: req.user.username || 'system',
                targetUsername: user.username
              });

              // Log action
              logPermissionAction(
                req.user.userId,
                'role_assigned',
                'user',
                parseInt(userId),
                { role: oldRole },
                { role },
                req
              );

              logger.info(`Role assigned: ${role} to user ${userId} by ${req.user.userId}`);

              res.json({ 
                message: 'Role assigned successfully',
                user_id: userId,
                old_role: oldRole,
                new_role: role
              });
            }
          );
        });
      });
    } catch (error) {
      logger.error('Role assignment error:', error);
      res.status(500).json({ error: 'Failed to assign role' });
    }
  }
);

/**
 * Get permission audit log
 * Admin only
 */
router.get('/audit-log',
  authenticate,
  requirePermission('security.view_audit'),
  readLimiter,
  async (req, res) => {
    const { limit = 100, offset = 0, userId, action, targetType } = req.query;

    try {
      let query = `
        SELECT pal.*, u.username 
        FROM permission_audit_log pal
        JOIN users u ON pal.user_id = u.id
        WHERE 1=1
      `;
      const params = [];

      if (userId) {
        query += ' AND pal.user_id = ?';
        params.push(userId);
      }

      if (action) {
        query += ' AND pal.action = ?';
        params.push(action);
      }

      if (targetType) {
        query += ' AND pal.target_type = ?';
        params.push(targetType);
      }

      query += ' ORDER BY pal.created_at DESC LIMIT ? OFFSET ?';
      params.push(parseInt(limit), parseInt(offset));

      db.all(query, params, (err, logs) => {
        if (err) {
          logger.error('Error fetching audit log:', err);
          return res.status(500).json({ error: 'Failed to fetch audit log' });
        }

        // Parse JSON fields
        const parsedLogs = logs.map(log => ({
          ...log,
          old_value: log.old_value ? JSON.parse(log.old_value) : null,
          new_value: log.new_value ? JSON.parse(log.new_value) : null
        }));

        res.json({ logs: parsedLogs, limit: parseInt(limit), offset: parseInt(offset) });
      });
    } catch (error) {
      logger.error('Audit log fetch error:', error);
      res.status(500).json({ error: 'Failed to fetch audit log' });
    }
  }
);

/**
 * Get permission statistics
 * Shows which permissions are most used
 */
router.get('/stats',
  authenticate,
  requirePermission('security.view_audit'),
  readLimiter,
  async (req, res) => {
    try {
      // Get role distribution
      db.all(
        `SELECT r.name, r.role_key, COUNT(u.id) as user_count
         FROM roles r
         LEFT JOIN users u ON r.role_key = u.role
         GROUP BY r.role_key
         ORDER BY user_count DESC`,
        [],
        (err, roleStats) => {
          if (err) {
            logger.error('Error fetching role stats:', err);
            return res.status(500).json({ error: 'Failed to fetch statistics' });
          }

          // Get recent permission actions
          db.all(
            `SELECT action, COUNT(*) as count
             FROM permission_audit_log
             WHERE created_at >= datetime('now', '-30 days')
             GROUP BY action
             ORDER BY count DESC`,
            [],
            (err, actionStats) => {
              if (err) {
                logger.error('Error fetching action stats:', err);
                return res.status(500).json({ error: 'Failed to fetch statistics' });
              }

              res.json({
                role_distribution: roleStats,
                recent_actions: actionStats,
                total_permissions: Object.keys(PERMISSIONS).length,
                total_roles: roleStats.length
              });
            }
          );
        }
      );
    } catch (error) {
      logger.error('Stats fetch error:', error);
      res.status(500).json({ error: 'Failed to fetch statistics' });
    }
  }
);

module.exports = router;
