const logger = require('../utils/logger');
const { db } = require('../database/db');

/**
 * RBAC (Role-Based Access Control) Middleware
 * 
 * Implements granular permission checking following the principle of least privilege.
 * Each user has roles, and each role has specific permissions.
 * This prevents over-privileged accounts and limits attack surface.
 */

// Define all available permissions in the system
const PERMISSIONS = {
  // User Management
  'users.view': 'View user list and details',
  'users.create': 'Create new users',
  'users.edit': 'Edit existing users',
  'users.delete': 'Delete users',
  'users.manage_roles': 'Assign and modify user roles',
  'users.unlock': 'Unlock locked user accounts',
  'users.reset_password': 'Reset user passwords',
  
  // Session Management
  'sessions.view_own': 'View own active sessions',
  'sessions.view_all': 'View all user sessions',
  'sessions.terminate_own': 'Terminate own sessions',
  'sessions.terminate_any': 'Terminate any user session',
  'sessions.view_stats': 'View session statistics',
  
  // Content Management
  'playlists.view': 'View playlists',
  'playlists.create': 'Create playlists',
  'playlists.edit': 'Edit playlists',
  'playlists.delete': 'Delete playlists',
  'playlists.import': 'Import M3U files',
  
  'channels.view': 'View channels',
  'channels.edit': 'Edit channel details',
  'channels.upload_logo': 'Upload custom channel logos',
  'channels.delete_logo': 'Delete custom channel logos',
  
  'favorites.view': 'View favorites',
  'favorites.manage': 'Add/remove favorites',
  
  'history.view_own': 'View own watch history',
  'history.view_all': 'View all user watch history',
  'history.delete_own': 'Delete own watch history',
  'history.delete_any': 'Delete any user watch history',
  
  // System & Settings
  'settings.view': 'View application settings',
  'settings.edit': 'Modify application settings',
  
  'stats.view': 'View analytics and statistics',
  'stats.view_detailed': 'View detailed analytics (user activity, etc.)',
  
  'backup.view': 'View available backups',
  'backup.create': 'Create backups',
  'backup.restore': 'Restore from backups',
  'backup.delete': 'Delete backups',
  'backup.download': 'Download backups',
  
  // Security Management
  'security.view_sessions': 'View security session dashboard',
  'security.view_csp': 'View CSP violation dashboard',
  'security.manage_2fa': 'Manage two-factor authentication',
  'security.view_audit': 'View audit logs',
  
  // Search & Discovery
  'search.use': 'Use search functionality',
  'search.admin': 'Search users and system settings',
  
  // VPN & Network
  'vpn.view': 'View VPN settings',
  'vpn.configure': 'Configure VPN settings',
  'vpn.connect': 'Connect/disconnect VPN'
};

// Default role definitions with their permissions
const DEFAULT_ROLES = {
  'admin': {
    name: 'Administrator',
    description: 'Full system access',
    permissions: Object.keys(PERMISSIONS), // Admins have all permissions
    is_system_role: true
  },
  'moderator': {
    name: 'Moderator',
    description: 'Content management and user support',
    permissions: [
      // User viewing (but not management)
      'users.view',
      
      // Content management
      'playlists.view',
      'playlists.create',
      'playlists.edit',
      'playlists.delete',
      'playlists.import',
      'channels.view',
      'channels.edit',
      'channels.upload_logo',
      'channels.delete_logo',
      
      // History management
      'history.view_all',
      'history.delete_any',
      
      // Settings (view only)
      'settings.view',
      
      // Statistics
      'stats.view',
      'stats.view_detailed',
      
      // Own sessions
      'sessions.view_own',
      'sessions.terminate_own',
      
      // Own favorites
      'favorites.view',
      'favorites.manage',
      
      // Search
      'search.use',
      
      // Own security
      'security.manage_2fa'
    ],
    is_system_role: true
  },
  'user': {
    name: 'Regular User',
    description: 'Standard user with content access',
    permissions: [
      // Own playlists
      'playlists.view',
      'playlists.create',
      'playlists.edit',
      'playlists.delete',
      'playlists.import',
      
      // Channels (view and customize)
      'channels.view',
      'channels.upload_logo',
      'channels.delete_logo',
      
      // Own favorites
      'favorites.view',
      'favorites.manage',
      
      // Own history
      'history.view_own',
      'history.delete_own',
      
      // Own settings
      'settings.view',
      'settings.edit',
      
      // Own sessions
      'sessions.view_own',
      'sessions.terminate_own',
      
      // Search (basic)
      'search.use',
      
      // Own security
      'security.manage_2fa',
      
      // VPN (if enabled)
      'vpn.view',
      'vpn.configure',
      'vpn.connect'
    ],
    is_system_role: true
  },
  'viewer': {
    name: 'Viewer',
    description: 'Read-only access for content viewing',
    permissions: [
      // View only
      'playlists.view',
      'channels.view',
      'favorites.view',
      'favorites.manage',
      'history.view_own',
      'history.delete_own',
      'settings.view',
      'sessions.view_own',
      'sessions.terminate_own',
      'search.use',
      'security.manage_2fa'
    ],
    is_system_role: true
  }
};

/**
 * Cache for user permissions to reduce database queries
 * Format: { userId: { permissions: [...], expires: timestamp } }
 */
const permissionCache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

/**
 * Clear permission cache for a specific user
 */
const clearUserPermissionCache = (userId) => {
  permissionCache.delete(userId);
  logger.info(`Permission cache cleared for user ${userId}`);
};

/**
 * Clear entire permission cache (call after role/permission changes)
 */
const clearAllPermissionCache = () => {
  permissionCache.clear();
  logger.info('All permission cache cleared');
};

/**
 * Get user permissions from database with caching
 */
const getUserPermissions = (userId) => {
  return new Promise((resolve, reject) => {
    // Check cache first
    const cached = permissionCache.get(userId);
    if (cached && cached.expires > Date.now()) {
      return resolve(cached.permissions);
    }

    // Query database
    db.get(
      `SELECT u.role, r.permissions 
       FROM users u
       LEFT JOIN roles r ON u.role = r.role_key
       WHERE u.id = ? AND u.is_active = 1`,
      [userId],
      (err, result) => {
        if (err) {
          logger.error('Error fetching user permissions:', err);
          return reject(err);
        }

        if (!result) {
          return reject(new Error('User not found or inactive'));
        }

        // Parse permissions (stored as JSON string)
        let permissions = [];
        try {
          if (result.permissions) {
            permissions = JSON.parse(result.permissions);
          } else {
            // Fallback to default role permissions if not in database
            const defaultRole = DEFAULT_ROLES[result.role];
            permissions = defaultRole ? defaultRole.permissions : [];
          }
        } catch (parseErr) {
          logger.error('Error parsing permissions:', parseErr);
          permissions = [];
        }

        // Cache the result
        permissionCache.set(userId, {
          permissions,
          expires: Date.now() + CACHE_TTL
        });

        resolve(permissions);
      }
    );
  });
};

/**
 * Check if user has a specific permission
 */
const hasPermission = async (userId, permission) => {
  try {
    const permissions = await getUserPermissions(userId);
    return permissions.includes(permission);
  } catch (error) {
    logger.error('Permission check failed:', error);
    return false;
  }
};

/**
 * Check if user has ANY of the specified permissions
 */
const hasAnyPermission = async (userId, permissionList) => {
  try {
    const permissions = await getUserPermissions(userId);
    return permissionList.some(p => permissions.includes(p));
  } catch (error) {
    logger.error('Permission check failed:', error);
    return false;
  }
};

/**
 * Check if user has ALL of the specified permissions
 */
const hasAllPermissions = async (userId, permissionList) => {
  try {
    const permissions = await getUserPermissions(userId);
    return permissionList.every(p => permissions.includes(p));
  } catch (error) {
    logger.error('Permission check failed:', error);
    return false;
  }
};

/**
 * Middleware: Require specific permission(s)
 * Usage: requirePermission('users.view')
 * Usage: requirePermission(['users.view', 'users.edit']) - requires ANY
 */
const requirePermission = (requiredPermissions) => {
  // Normalize to array
  const permissionList = Array.isArray(requiredPermissions) 
    ? requiredPermissions 
    : [requiredPermissions];

  return async (req, res, next) => {
    if (!req.user || !req.user.userId) {
      return res.status(401).json({ 
        error: 'Authentication required',
        code: 'AUTH_REQUIRED'
      });
    }

    try {
      // Bypass permission check for user ID 1 (first admin) or users with role 'admin'
      if (req.user.userId === 1 || req.user.role === 'admin') {
        req.userPermissions = Object.keys(PERMISSIONS); // Grant all permissions
        return next();
      }

      const userPermissions = await getUserPermissions(req.user.userId);
      
      // Check if user has any of the required permissions
      const hasAccess = permissionList.some(p => userPermissions.includes(p));
      
      if (!hasAccess) {
        logger.warn(`Access denied: User ${req.user.userId} lacks permission(s): ${permissionList.join(', ')}`);
        return res.status(403).json({ 
          error: 'Insufficient permissions',
          code: 'INSUFFICIENT_PERMISSIONS',
          required: permissionList,
          details: 'You do not have the required permissions to perform this action'
        });
      }

      // Attach user permissions to request for further checks
      req.userPermissions = userPermissions;
      next();
    } catch (error) {
      logger.error('Permission check error:', error);
      res.status(500).json({ 
        error: 'Permission validation failed',
        code: 'PERMISSION_CHECK_FAILED'
      });
    }
  };
};

/**
 * Middleware: Require ALL specified permissions
 * Usage: requireAllPermissions(['users.view', 'users.edit'])
 */
const requireAllPermissions = (permissionList) => {
  return async (req, res, next) => {
    if (!req.user || !req.user.userId) {
      return res.status(401).json({ 
        error: 'Authentication required',
        code: 'AUTH_REQUIRED'
      });
    }

    try {
      // Bypass permission check for user ID 1 (first admin) or users with role 'admin'
      if (req.user.userId === 1 || req.user.role === 'admin') {
        req.userPermissions = Object.keys(PERMISSIONS); // Grant all permissions
        return next();
      }

      const userPermissions = await getUserPermissions(req.user.userId);
      
      // Check if user has ALL required permissions
      const hasAccess = permissionList.every(p => userPermissions.includes(p));
      
      if (!hasAccess) {
        logger.warn(`Access denied: User ${req.user.userId} lacks all permissions: ${permissionList.join(', ')}`);
        return res.status(403).json({ 
          error: 'Insufficient permissions',
          code: 'INSUFFICIENT_PERMISSIONS',
          required: permissionList,
          details: 'You do not have all the required permissions to perform this action'
        });
      }

      req.userPermissions = userPermissions;
      next();
    } catch (error) {
      logger.error('Permission check error:', error);
      res.status(500).json({ 
        error: 'Permission validation failed',
        code: 'PERMISSION_CHECK_FAILED'
      });
    }
  };
};

/**
 * Initialize roles table and seed default roles
 */
const initializeRoles = () => {
  db.serialize(() => {
    // Create roles table
    db.run(`CREATE TABLE IF NOT EXISTS roles (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      role_key TEXT UNIQUE NOT NULL,
      name TEXT NOT NULL,
      description TEXT,
      permissions TEXT NOT NULL,
      is_system_role BOOLEAN DEFAULT 0,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )`, (err) => {
      if (err) {
        logger.error('Failed to create roles table:', err);
        return;
      }

      // Seed default roles
      Object.entries(DEFAULT_ROLES).forEach(([roleKey, roleData]) => {
        db.run(
          `INSERT OR IGNORE INTO roles (role_key, name, description, permissions, is_system_role) 
           VALUES (?, ?, ?, ?, ?)`,
          [
            roleKey,
            roleData.name,
            roleData.description,
            JSON.stringify(roleData.permissions),
            roleData.is_system_role ? 1 : 0
          ],
          (err) => {
            if (err) {
              logger.error(`Failed to seed role ${roleKey}:`, err);
            } else {
              logger.info(`✓ Role seeded: ${roleKey}`);
            }
          }
        );
      });

      // Create permission audit log table
      db.run(`CREATE TABLE IF NOT EXISTS permission_audit_log (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id INTEGER NOT NULL,
        action TEXT NOT NULL,
        target_type TEXT NOT NULL,
        target_id INTEGER,
        old_value TEXT,
        new_value TEXT,
        ip_address TEXT,
        user_agent TEXT,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
      )`, (err) => {
        if (err) {
          logger.error('Failed to create permission_audit_log table:', err);
        } else {
          logger.info('✓ Permission audit log table created');
        }
      });
    });
  });
};

/**
 * Log permission-related actions for audit trail
 */
const logPermissionAction = (userId, action, targetType, targetId, oldValue, newValue, req) => {
  const ipAddress = req?.ip || req?.connection?.remoteAddress || 'unknown';
  const userAgent = req?.headers['user-agent'] || 'unknown';

  db.run(
    `INSERT INTO permission_audit_log 
     (user_id, action, target_type, target_id, old_value, new_value, ip_address, user_agent)
     VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
    [
      userId,
      action,
      targetType,
      targetId || null,
      oldValue ? JSON.stringify(oldValue) : null,
      newValue ? JSON.stringify(newValue) : null,
      ipAddress,
      userAgent
    ],
    (err) => {
      if (err) {
        logger.error('Failed to log permission action:', err);
      }
    }
  );
};

module.exports = {
  PERMISSIONS,
  DEFAULT_ROLES,
  requirePermission,
  requireAllPermissions,
  hasPermission,
  hasAnyPermission,
  hasAllPermissions,
  getUserPermissions,
  clearUserPermissionCache,
  clearAllPermissionCache,
  initializeRoles,
  logPermissionAction
};
