import { getAuthenticatedClient } from '../../../scripts/utils/directus-client';
import { rbacService, Permission } from './rbac.service';
import { auditLogger } from '../../workflow/auditLogger';
import { webhookService } from '../../workflow/webhookService';
import { notificationService, NotificationType, NotificationPriority } from '../../notifications/notificationService';

// Import Directus SDK functions
const directusSdk = require('@directus/sdk');
const readItem = directusSdk.readItem as any;
const readItems = directusSdk.readItems as any;
const updateItem = directusSdk.updateItem as any;
const aggregate = directusSdk.aggregate as any;

/**
 * Task Assignment Interface
 */
export interface TaskAssignment {
  taskId: string;
  assigneeId: string;
  assignerId: string;
  assignedAt: string;
  dueDate?: string;
  workloadPoints?: number;
  notes?: string;
}

/**
 * User Workload Summary
 */
export interface WorkloadSummary {
  userId: string;
  totalTasks: number;
  activeTasks: number;
  completedTasks: number;
  overdueTasks: number;
  totalWorkloadPoints: number;
  averageCompletionTime?: number;
  upcomingDeadlines: {
    taskId: string;
    title: string;
    dueDate: string;
  }[];
}

/**
 * Assignment Recommendation
 */
export interface AssignmentRecommendation {
  userId: string;
  userName: string;
  currentWorkload: number;
  availability: number;
  skillMatch?: number;
  recommendationScore: number;
  reason: string;
}

/**
 * Assignment Service
 * Manages task assignments and workload distribution
 */
export class AssignmentService {
  private client: any;

  constructor() {
    this.initializeClient();
  }

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

  /**
   * Assign a task to a user
   */
  async assignTask(
    taskId: string,
    assigneeId: string,
    assignerId: string,
    options?: {
      dueDate?: string;
      workloadPoints?: number;
      notes?: string;
    }
  ): Promise<TaskAssignment> {
    if (!this.client) await this.initializeClient();

    try {
      // Check if assigner has permission to assign
      const canAssign = await rbacService.hasPermission(assignerId, Permission.ASSIGN_TASK);
      if (!canAssign) {
        // Check if it's a project lead assigning in their project
        const task = await this.client.request(
          readItem('tasks', taskId, { fields: ['project'] })
        );
        
        const canAssignInProject = await rbacService.canAssignTasks(assignerId, task.project);
        if (!canAssignInProject) {
          throw new Error('Permission denied: Cannot assign tasks');
        }
      }

      // Update task with assignment
      const updatedTask = await this.client.request(
        updateItem('tasks', taskId, {
          assigned_to: assigneeId,
          assigned_by: assignerId,
          assigned_at: new Date().toISOString(),
          due_date: options?.dueDate,
          workload_points: options?.workloadPoints,
          assignment_notes: options?.notes,
          updated_at: new Date().toISOString()
        })
      );

      // Create assignment record
      const assignment: TaskAssignment = {
        taskId,
        assigneeId,
        assignerId,
        assignedAt: updatedTask.assigned_at,
        dueDate: options?.dueDate,
        workloadPoints: options?.workloadPoints,
        notes: options?.notes
      };

      // Log the assignment
      await auditLogger.logTaskAction(
        taskId,
        'task_assigned',
        assignerId,
        {
          assigneeId,
          dueDate: options?.dueDate,
          workloadPoints: options?.workloadPoints
        }
      );

      // Trigger webhooks
      await webhookService.triggerWebhooks('task.assigned', taskId, {
        task: updatedTask,
        assignment,
        currentStatus: updatedTask.status
      });

      // Send notification to assignee
      await notificationService.sendNotification(assigneeId, {
        type: NotificationType.TASK_ASSIGNED,
        priority: updatedTask.priority === 'high' ? NotificationPriority.HIGH : 
                 updatedTask.priority === 'urgent' ? NotificationPriority.URGENT :
                 NotificationPriority.MEDIUM,
        title: 'New Task Assigned',
        message: `You have been assigned to task: ${updatedTask.title}`,
        actionUrl: `/tasks/${taskId}`,
        metadata: {
          taskId,
          taskTitle: updatedTask.title,
          assignerId,
          dueDate: options?.dueDate,
          workloadPoints: options?.workloadPoints
        },
        actions: [{
          label: 'View Task',
          action: 'view_task',
          url: `/tasks/${taskId}`
        }]
      });

      return assignment;
    } catch (error: any) {
      throw new Error(`Failed to assign task: ${error.message}`);
    }
  }

  /**
   * Reassign a task to a different user
   */
  async reassignTask(
    taskId: string,
    newAssigneeId: string,
    reassignerId: string,
    reason?: string
  ): Promise<TaskAssignment> {
    if (!this.client) await this.initializeClient();

    try {
      // Get current assignment
      const currentTask = await this.client.request(
        readItem('tasks', taskId, {
          fields: ['assigned_to', 'project', 'workload_points', 'due_date']
        })
      );

      const previousAssignee = currentTask.assigned_to;

      // Perform the reassignment
      const assignment = await this.assignTask(
        taskId,
        newAssigneeId,
        reassignerId,
        {
          dueDate: currentTask.due_date,
          workloadPoints: currentTask.workload_points,
          notes: reason || 'Task reassigned'
        }
      );

      // Log the reassignment
      await auditLogger.logTaskAction(
        taskId,
        'task_reassigned',
        reassignerId,
        {
          previousAssignee,
          newAssignee: newAssigneeId,
          reason
        }
      );

      return assignment;
    } catch (error: any) {
      throw new Error(`Failed to reassign task: ${error.message}`);
    }
  }

  /**
   * Unassign a task
   */
  async unassignTask(taskId: string, unassignerId: string): Promise<void> {
    if (!this.client) await this.initializeClient();

    try {
      // Get current task
      const currentTask = await this.client.request(
        readItem('tasks', taskId, {
          fields: ['assigned_to', 'project']
        })
      );

      // Check permission
      const canAssign = await rbacService.canAssignTasks(unassignerId, currentTask.project);
      if (!canAssign) {
        throw new Error('Permission denied: Cannot unassign tasks');
      }

      // Update task to remove assignment
      await this.client.request(
        updateItem('tasks', taskId, {
          assigned_to: null,
          assigned_by: null,
          assigned_at: null,
          assignment_notes: null,
          updated_at: new Date().toISOString()
        })
      );

      // Log the unassignment
      await auditLogger.logTaskAction(
        taskId,
        'task_unassigned',
        unassignerId,
        {
          previousAssignee: currentTask.assigned_to
        }
      );
    } catch (error: any) {
      throw new Error(`Failed to unassign task: ${error.message}`);
    }
  }

  /**
   * Get tasks assigned to a user
   */
  async getAssignedTasks(
    userId: string,
    options?: {
      includeCompleted?: boolean;
      projectId?: string;
      status?: string;
      sort?: string;
      limit?: number;
    }
  ): Promise<any[]> {
    if (!this.client) await this.initializeClient();

    try {
      // Build filter
      const filter: any = {
        assigned_to: { _eq: userId }
      };

      if (!options?.includeCompleted) {
        filter.status = { _neq: 'done' };
      } else if (options?.status) {
        filter.status = { _eq: options.status };
      }

      if (options?.projectId) {
        filter.project = { _eq: options.projectId };
      }

      // Query tasks
      const tasks = await this.client.request(
        readItems('tasks', {
          filter,
          sort: options?.sort ? [options.sort] : ['-priority', 'due_date'],
          limit: options?.limit || 100
        })
      );

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

  /**
   * Get workload summary for a user
   */
  async getWorkloadSummary(userId: string): Promise<WorkloadSummary> {
    if (!this.client) await this.initializeClient();

    try {
      // Get all tasks assigned to user
      const tasks = await this.client.request(
        readItems('tasks', {
          filter: {
            assigned_to: { _eq: userId }
          },
          fields: ['*']
        })
      );

      // Calculate metrics
      const now = new Date();
      const activeTasks = tasks.filter((t: any) => 
        t.status !== 'done' && t.status !== 'cancelled'
      );
      const completedTasks = tasks.filter((t: any) => t.status === 'done');
      const overdueTasks = activeTasks.filter((t: any) => 
        t.due_date && new Date(t.due_date) < now
      );

      // Calculate total workload points
      const totalWorkloadPoints = activeTasks.reduce((sum: number, task: any) => 
        sum + (task.workload_points || 1), 0
      );

      // Calculate average completion time
      let averageCompletionTime: number | undefined;
      if (completedTasks.length > 0) {
        const completionTimes = completedTasks
          .filter((t: any) => t.assigned_at && t.completed_at)
          .map((t: any) => {
            const assigned = new Date(t.assigned_at).getTime();
            const completed = new Date(t.completed_at).getTime();
            return (completed - assigned) / (1000 * 60 * 60); // Hours
          });
        
        if (completionTimes.length > 0) {
          averageCompletionTime = completionTimes.reduce((a, b) => a + b, 0) / 
                                 completionTimes.length;
        }
      }

      // Get upcoming deadlines
      const upcomingDeadlines = activeTasks
        .filter((t: any) => t.due_date)
        .sort((a: any, b: any) => 
          new Date(a.due_date).getTime() - new Date(b.due_date).getTime()
        )
        .slice(0, 5)
        .map((t: any) => ({
          taskId: t.id,
          title: t.title,
          dueDate: t.due_date
        }));

      return {
        userId,
        totalTasks: tasks.length,
        activeTasks: activeTasks.length,
        completedTasks: completedTasks.length,
        overdueTasks: overdueTasks.length,
        totalWorkloadPoints,
        averageCompletionTime,
        upcomingDeadlines
      };
    } catch (error: any) {
      throw new Error(`Failed to get workload summary: ${error.message}`);
    }
  }

  /**
   * Get assignment recommendations for a task
   */
  async getAssignmentRecommendations(
    taskId: string,
    options?: {
      maxRecommendations?: number;
      projectTeamOnly?: boolean;
    }
  ): Promise<AssignmentRecommendation[]> {
    if (!this.client) await this.initializeClient();

    try {
      // Get task details
      const task = await this.client.request(
        readItem('tasks', taskId, {
          fields: ['project', 'priority', 'complexity', 'required_skills']
        })
      );

      // Get potential assignees
      let users: any[];
      if (options?.projectTeamOnly) {
        // Get project team members
        users = await this.getProjectTeamMembers(task.project);
      } else {
        // Get all active users
        users = await this.client.request(
          readItems('directus_users', {
            filter: { status: { _eq: 'active' } },
            fields: ['id', 'first_name', 'last_name', 'email']
          })
        );
      }

      // Calculate recommendations
      const recommendations: AssignmentRecommendation[] = [];

      for (const user of users) {
        const workloadSummary = await this.getWorkloadSummary(user.id);
        
        // Calculate availability score (inverse of workload)
        const maxWorkload = 20; // Maximum reasonable task count
        const availability = Math.max(0, 100 * (1 - workloadSummary.activeTasks / maxWorkload));
        
        // Calculate recommendation score
        let recommendationScore = availability;
        let reason = '';

        if (workloadSummary.activeTasks === 0) {
          recommendationScore += 20;
          reason = 'No current assignments';
        } else if (workloadSummary.activeTasks < 3) {
          recommendationScore += 10;
          reason = 'Light workload';
        } else if (workloadSummary.activeTasks > 10) {
          recommendationScore -= 20;
          reason = 'Heavy workload';
        } else {
          reason = 'Moderate workload';
        }

        // Check for overdue tasks (penalty)
        if (workloadSummary.overdueTasks > 0) {
          recommendationScore -= 10 * workloadSummary.overdueTasks;
          reason += `, ${workloadSummary.overdueTasks} overdue tasks`;
        }

        recommendations.push({
          userId: user.id,
          userName: `${user.first_name} ${user.last_name}`.trim() || user.email,
          currentWorkload: workloadSummary.activeTasks,
          availability,
          recommendationScore: Math.max(0, Math.min(100, recommendationScore)),
          reason
        });
      }

      // Sort by recommendation score
      recommendations.sort((a, b) => b.recommendationScore - a.recommendationScore);

      // Return top recommendations
      return recommendations.slice(0, options?.maxRecommendations || 5);
    } catch (error: any) {
      throw new Error(`Failed to get assignment recommendations: ${error.message}`);
    }
  }

  /**
   * Batch assign multiple tasks
   */
  async batchAssignTasks(
    assignments: {
      taskId: string;
      assigneeId: string;
      dueDate?: string;
      workloadPoints?: number;
    }[],
    assignerId: string
  ): Promise<TaskAssignment[]> {
    const results: TaskAssignment[] = [];

    for (const assignment of assignments) {
      try {
        const result = await this.assignTask(
          assignment.taskId,
          assignment.assigneeId,
          assignerId,
          {
            dueDate: assignment.dueDate,
            workloadPoints: assignment.workloadPoints
          }
        );
        results.push(result);
      } catch (error) {
        console.error(`Failed to assign task ${assignment.taskId}:`, error);
      }
    }

    return results;
  }

  /**
   * Auto-balance workload across team
   */
  async autoBalanceWorkload(
    projectId: string,
    balancerId: string
  ): Promise<{ reassignments: number; details: any[] }> {
    if (!this.client) await this.initializeClient();

    try {
      // Check permission
      const canAssign = await rbacService.canAssignTasks(balancerId, projectId);
      if (!canAssign) {
        throw new Error('Permission denied: Cannot balance workload');
      }

      // Get unassigned tasks in project
      const unassignedTasks = await this.client.request(
        readItems('tasks', {
          filter: {
            project: { _eq: projectId },
            assigned_to: { _null: true },
            status: { _neq: 'done' }
          }
        })
      );

      // Get team members
      const teamMembers = await this.getProjectTeamMembers(projectId);
      
      const reassignments: any[] = [];

      // Assign unassigned tasks to least loaded team members
      for (const task of unassignedTasks) {
        const recommendations = await this.getAssignmentRecommendations(
          task.id,
          { maxRecommendations: 1, projectTeamOnly: true }
        );

        if (recommendations.length > 0) {
          const best = recommendations[0];
          await this.assignTask(
            task.id,
            best.userId,
            balancerId,
            { notes: `Auto-balanced: ${best.reason}` }
          );

          reassignments.push({
            taskId: task.id,
            assigneeId: best.userId,
            reason: best.reason
          });
        }
      }

      return {
        reassignments: reassignments.length,
        details: reassignments
      };
    } catch (error: any) {
      throw new Error(`Failed to auto-balance workload: ${error.message}`);
    }
  }

  /**
   * Get project team members
   */
  private async getProjectTeamMembers(projectId: string): Promise<any[]> {
    if (!this.client) await this.initializeClient();

    try {
      // This would query a project_members table
      // For now, returning all active users
      const users = await this.client.request(
        readItems('directus_users', {
          filter: { status: { _eq: 'active' } },
          fields: ['id', 'first_name', 'last_name', 'email']
        })
      );

      return users;
    } catch (error) {
      console.error('Failed to get project team members:', error);
      return [];
    }
  }
}

// Export singleton instance
export const assignmentService = new AssignmentService();