import { getAuthenticatedClient } from '../../../scripts/utils/directus-client';
import * as jwt from 'jsonwebtoken';

// Import Directus SDK functions
const directusSdk = require('@directus/sdk');
const readMe = directusSdk.readMe as any;
const readRole = directusSdk.readRole as any;
const readRoles = directusSdk.readRoles as any;
const readPermissions = directusSdk.readPermissions as any;
const readItem = directusSdk.readItem as any;

/**
 * User Role Definitions
 */
export enum UserRole {
  ADMIN = 'admin',
  TASK_MANAGER = 'task_manager',
  PROJECT_LEAD = 'project_lead',
  TEAM_MEMBER = 'team_member',
  VIEWER = 'viewer'
}

/**
 * Permission Types
 */
export enum Permission {
  // Task permissions
  CREATE_TASK = 'task.create',
  READ_TASK = 'task.read',
  UPDATE_TASK = 'task.update',
  DELETE_TASK = 'task.delete',
  ASSIGN_TASK = 'task.assign',
  
  // Project permissions
  CREATE_PROJECT = 'project.create',
  READ_PROJECT = 'project.read',
  UPDATE_PROJECT = 'project.update',
  DELETE_PROJECT = 'project.delete',
  MANAGE_PROJECT_MEMBERS = 'project.manage_members',
  
  // User management permissions
  MANAGE_USERS = 'user.manage',
  VIEW_USERS = 'user.view',
  
  // Workflow permissions
  MANAGE_WORKFLOWS = 'workflow.manage',
  TRIGGER_TRANSITIONS = 'workflow.trigger',
  
  // Reporting permissions
  VIEW_REPORTS = 'reports.view',
  EXPORT_REPORTS = 'reports.export',
  
  // System permissions
  MANAGE_SYSTEM = 'system.manage',
  VIEW_AUDIT_LOGS = 'audit.view'
}

/**
 * Role-Permission Mapping
 */
const ROLE_PERMISSIONS: Record<UserRole, Permission[]> = {
  [UserRole.ADMIN]: [
    // Admin has all permissions
    ...Object.values(Permission)
  ],
  [UserRole.TASK_MANAGER]: [
    Permission.CREATE_TASK,
    Permission.READ_TASK,
    Permission.UPDATE_TASK,
    Permission.DELETE_TASK,
    Permission.ASSIGN_TASK,
    Permission.CREATE_PROJECT,
    Permission.READ_PROJECT,
    Permission.UPDATE_PROJECT,
    Permission.MANAGE_PROJECT_MEMBERS,
    Permission.VIEW_USERS,
    Permission.MANAGE_WORKFLOWS,
    Permission.TRIGGER_TRANSITIONS,
    Permission.VIEW_REPORTS,
    Permission.EXPORT_REPORTS,
    Permission.VIEW_AUDIT_LOGS
  ],
  [UserRole.PROJECT_LEAD]: [
    Permission.CREATE_TASK,
    Permission.READ_TASK,
    Permission.UPDATE_TASK,
    Permission.ASSIGN_TASK,
    Permission.READ_PROJECT,
    Permission.UPDATE_PROJECT,
    Permission.MANAGE_PROJECT_MEMBERS,
    Permission.VIEW_USERS,
    Permission.TRIGGER_TRANSITIONS,
    Permission.VIEW_REPORTS,
    Permission.EXPORT_REPORTS
  ],
  [UserRole.TEAM_MEMBER]: [
    Permission.READ_TASK,
    Permission.UPDATE_TASK,
    Permission.READ_PROJECT,
    Permission.VIEW_USERS,
    Permission.VIEW_REPORTS
  ],
  [UserRole.VIEWER]: [
    Permission.READ_TASK,
    Permission.READ_PROJECT,
    Permission.VIEW_USERS,
    Permission.VIEW_REPORTS
  ]
};

/**
 * User Context Interface
 */
export interface UserContext {
  id: string;
  email: string;
  role: UserRole;
  permissions: Permission[];
  projects?: string[];
  teams?: string[];
}

/**
 * RBAC Service
 * Handles role-based access control and permission management
 */
export class RBACService {
  private client: any;
  private userCache: Map<string, UserContext> = new Map();
  private cacheTimeout: number = 5 * 60 * 1000; // 5 minutes

  constructor() {
    this.initializeClient();
  }

  private async initializeClient() {
    this.client = await getAuthenticatedClient();
  }

  /**
   * Get user context with role and permissions
   */
  async getUserContext(userId: string): Promise<UserContext> {
    // Check cache first
    const cached = this.userCache.get(userId);
    if (cached) {
      return cached;
    }

    if (!this.client) await this.initializeClient();

    try {
      // Fetch user with role information
      const user = await this.client.request(
        readItem('directus_users', userId, {
          fields: ['*', 'role.*']
        })
      );

      if (!user) {
        throw new Error('User not found');
      }

      // Map Directus role to our UserRole enum
      const userRole = this.mapDirectusRole(user.role);
      
      // Get permissions for the role
      const permissions = ROLE_PERMISSIONS[userRole] || [];

      // Get user's project and team assignments
      const projects = await this.getUserProjects(userId);
      const teams = await this.getUserTeams(userId);

      const context: UserContext = {
        id: user.id,
        email: user.email,
        role: userRole,
        permissions,
        projects,
        teams
      };

      // Cache the context
      this.userCache.set(userId, context);
      setTimeout(() => this.userCache.delete(userId), this.cacheTimeout);

      return context;
    } catch (error: any) {
      throw new Error(`Failed to get user context: ${error.message}`);
    }
  }

  /**
   * Check if user has a specific permission
   */
  async hasPermission(userId: string, permission: Permission): Promise<boolean> {
    try {
      const context = await this.getUserContext(userId);
      return context.permissions.includes(permission);
    } catch (error) {
      console.error('Permission check failed:', error);
      return false;
    }
  }

  /**
   * Check if user has any of the specified permissions
   */
  async hasAnyPermission(userId: string, permissions: Permission[]): Promise<boolean> {
    try {
      const context = await this.getUserContext(userId);
      return permissions.some(permission => context.permissions.includes(permission));
    } catch (error) {
      console.error('Permission check failed:', error);
      return false;
    }
  }

  /**
   * Check if user has all of the specified permissions
   */
  async hasAllPermissions(userId: string, permissions: Permission[]): Promise<boolean> {
    try {
      const context = await this.getUserContext(userId);
      return permissions.every(permission => context.permissions.includes(permission));
    } catch (error) {
      console.error('Permission check failed:', error);
      return false;
    }
  }

  /**
   * Check if user can access a specific task
   */
  async canAccessTask(userId: string, taskId: string): Promise<boolean> {
    try {
      const context = await this.getUserContext(userId);
      
      // Admins and Task Managers can access all tasks
      if (context.role === UserRole.ADMIN || context.role === UserRole.TASK_MANAGER) {
        return true;
      }

      // Check if user has read permission
      if (!context.permissions.includes(Permission.READ_TASK)) {
        return false;
      }

      // Fetch the task to check ownership/assignment
      const task = await this.client.request(
        readItem('tasks', taskId, {
          fields: ['assigned_to', 'project', 'created_by']
        })
      );

      if (!task) {
        return false;
      }

      // Check if user is assigned to the task
      if (task.assigned_to === userId) {
        return true;
      }

      // Check if user is the creator
      if (task.created_by === userId) {
        return true;
      }

      // Check if user is a project lead for the task's project
      if (context.role === UserRole.PROJECT_LEAD && 
          context.projects?.includes(task.project)) {
        return true;
      }

      // Team members can see tasks in their projects
      if (context.role === UserRole.TEAM_MEMBER && 
          context.projects?.includes(task.project)) {
        return true;
      }

      return false;
    } catch (error) {
      console.error('Task access check failed:', error);
      return false;
    }
  }

  /**
   * Check if user can modify a specific task
   */
  async canModifyTask(userId: string, taskId: string): Promise<boolean> {
    try {
      const context = await this.getUserContext(userId);
      
      // Check update permission
      if (!context.permissions.includes(Permission.UPDATE_TASK)) {
        return false;
      }

      // Admins and Task Managers can modify all tasks
      if (context.role === UserRole.ADMIN || context.role === UserRole.TASK_MANAGER) {
        return true;
      }

      // Fetch the task
      const task = await this.client.request(
        readItem('tasks', taskId, {
          fields: ['assigned_to', 'project', 'created_by']
        })
      );

      if (!task) {
        return false;
      }

      // Project leads can modify tasks in their projects
      if (context.role === UserRole.PROJECT_LEAD && 
          context.projects?.includes(task.project)) {
        return true;
      }

      // Team members can only modify tasks assigned to them
      if (context.role === UserRole.TEAM_MEMBER && 
          task.assigned_to === userId) {
        return true;
      }

      return false;
    } catch (error) {
      console.error('Task modify check failed:', error);
      return false;
    }
  }

  /**
   * Verify JWT token and return user ID
   */
  async verifyToken(token: string): Promise<string | null> {
    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key') as any;
      return decoded.userId || null;
    } catch (error) {
      return null;
    }
  }

  /**
   * Check if user can assign tasks
   */
  async canAssignTasks(userId: string, projectId?: string): Promise<boolean> {
    try {
      const context = await this.getUserContext(userId);
      
      // Check assign permission
      if (!context.permissions.includes(Permission.ASSIGN_TASK)) {
        return false;
      }

      // Admins and Task Managers can assign any tasks
      if (context.role === UserRole.ADMIN || context.role === UserRole.TASK_MANAGER) {
        return true;
      }

      // Project leads can assign tasks in their projects
      if (context.role === UserRole.PROJECT_LEAD) {
        if (!projectId) {
          return !!(context.projects && context.projects.length > 0);
        }
        return context.projects?.includes(projectId) || false;
      }

      return false;
    } catch (error) {
      console.error('Task assignment check failed:', error);
      return false;
    }
  }

  /**
   * Get tasks accessible to a user
   */
  async getAccessibleTasks(userId: string): Promise<any> {
    try {
      const context = await this.getUserContext(userId);
      
      // Build filter based on role
      let filter: any = {};

      switch (context.role) {
        case UserRole.ADMIN:
        case UserRole.TASK_MANAGER:
          // No filter - can see all tasks
          break;
        
        case UserRole.PROJECT_LEAD:
          // Can see tasks in their projects
          if (context.projects && context.projects.length > 0) {
            filter.project = { _in: context.projects };
          } else {
            filter.id = { _eq: 'none' }; // No access
          }
          break;
        
        case UserRole.TEAM_MEMBER:
          // Can see assigned tasks and tasks in their projects
          filter._or = [
            { assigned_to: { _eq: userId } },
            { project: { _in: context.projects || [] } }
          ];
          break;
        
        case UserRole.VIEWER:
          // Can see tasks in their projects (read-only)
          if (context.projects && context.projects.length > 0) {
            filter.project = { _in: context.projects };
          } else {
            filter.id = { _eq: 'none' }; // No access
          }
          break;
        
        default:
          filter.id = { _eq: 'none' }; // No access
      }

      return filter;
    } catch (error) {
      console.error('Failed to get accessible tasks filter:', error);
      return { id: { _eq: 'none' } };
    }
  }

  /**
   * Map Directus role to UserRole enum
   */
  private mapDirectusRole(directusRole: any): UserRole {
    if (!directusRole || !directusRole.name) {
      return UserRole.VIEWER;
    }

    const roleName = directusRole.name.toLowerCase();
    
    if (roleName.includes('admin')) {
      return UserRole.ADMIN;
    }
    if (roleName.includes('manager')) {
      return UserRole.TASK_MANAGER;
    }
    if (roleName.includes('lead')) {
      return UserRole.PROJECT_LEAD;
    }
    if (roleName.includes('member') || roleName.includes('developer')) {
      return UserRole.TEAM_MEMBER;
    }
    
    return UserRole.VIEWER;
  }

  /**
   * Get user's projects
   */
  private async getUserProjects(userId: string): Promise<string[]> {
    if (!this.client) await this.initializeClient();

    try {
      // This would query a project_members table or similar
      // For now, returning empty array
      return [];
    } catch (error) {
      console.error('Failed to get user projects:', error);
      return [];
    }
  }

  /**
   * Get user's teams
   */
  private async getUserTeams(userId: string): Promise<string[]> {
    if (!this.client) await this.initializeClient();

    try {
      // This would query a team_members table or similar
      // For now, returning empty array
      return [];
    } catch (error) {
      console.error('Failed to get user teams:', error);
      return [];
    }
  }

  /**
   * Clear user cache
   */
  clearUserCache(userId?: string): void {
    if (userId) {
      this.userCache.delete(userId);
    } else {
      this.userCache.clear();
    }
  }

  /**
   * Get all available roles
   */
  async getAllRoles(): Promise<any[]> {
    if (!this.client) await this.initializeClient();

    try {
      const roles = await this.client.request(readRoles());
      return roles;
    } catch (error: any) {
      throw new Error(`Failed to get roles: ${error.message}`);
    }
  }

  /**
   * Get permissions for a role
   */
  getPermissionsForRole(role: UserRole): Permission[] {
    return ROLE_PERMISSIONS[role] || [];
  }
}

// Export singleton instance
export const rbacService = new RBACService();