"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.assignmentService = exports.AssignmentService = void 0;
const directus_client_1 = require("../../../scripts/utils/directus-client");
const rbac_service_1 = require("./rbac.service");
const auditLogger_1 = require("../../workflow/auditLogger");
const webhookService_1 = require("../../workflow/webhookService");
const notificationService_1 = require("../../notifications/notificationService");
// Import Directus SDK functions
const directusSdk = require('@directus/sdk');
const readItem = directusSdk.readItem;
const readItems = directusSdk.readItems;
const updateItem = directusSdk.updateItem;
const aggregate = directusSdk.aggregate;
/**
 * Assignment Service
 * Manages task assignments and workload distribution
 */
class AssignmentService {
    constructor() {
        this.initializeClient();
    }
    async initializeClient() {
        this.client = await (0, directus_client_1.getAuthenticatedClient)();
    }
    /**
     * Assign a task to a user
     */
    async assignTask(taskId, assigneeId, assignerId, options) {
        if (!this.client)
            await this.initializeClient();
        try {
            // Check if assigner has permission to assign
            const canAssign = await rbac_service_1.rbacService.hasPermission(assignerId, rbac_service_1.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 rbac_service_1.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 = {
                taskId,
                assigneeId,
                assignerId,
                assignedAt: updatedTask.assigned_at,
                dueDate: options?.dueDate,
                workloadPoints: options?.workloadPoints,
                notes: options?.notes
            };
            // Log the assignment
            await auditLogger_1.auditLogger.logTaskAction(taskId, 'task_assigned', assignerId, {
                assigneeId,
                dueDate: options?.dueDate,
                workloadPoints: options?.workloadPoints
            });
            // Trigger webhooks
            await webhookService_1.webhookService.triggerWebhooks('task.assigned', taskId, {
                task: updatedTask,
                assignment,
                currentStatus: updatedTask.status
            });
            // Send notification to assignee
            await notificationService_1.notificationService.sendNotification(assigneeId, {
                type: notificationService_1.NotificationType.TASK_ASSIGNED,
                priority: updatedTask.priority === 'high' ? notificationService_1.NotificationPriority.HIGH :
                    updatedTask.priority === 'urgent' ? notificationService_1.NotificationPriority.URGENT :
                        notificationService_1.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) {
            throw new Error(`Failed to assign task: ${error.message}`);
        }
    }
    /**
     * Reassign a task to a different user
     */
    async reassignTask(taskId, newAssigneeId, reassignerId, reason) {
        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_1.auditLogger.logTaskAction(taskId, 'task_reassigned', reassignerId, {
                previousAssignee,
                newAssignee: newAssigneeId,
                reason
            });
            return assignment;
        }
        catch (error) {
            throw new Error(`Failed to reassign task: ${error.message}`);
        }
    }
    /**
     * Unassign a task
     */
    async unassignTask(taskId, unassignerId) {
        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 rbac_service_1.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_1.auditLogger.logTaskAction(taskId, 'task_unassigned', unassignerId, {
                previousAssignee: currentTask.assigned_to
            });
        }
        catch (error) {
            throw new Error(`Failed to unassign task: ${error.message}`);
        }
    }
    /**
     * Get tasks assigned to a user
     */
    async getAssignedTasks(userId, options) {
        if (!this.client)
            await this.initializeClient();
        try {
            // Build filter
            const filter = {
                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) {
            throw new Error(`Failed to get assigned tasks: ${error.message}`);
        }
    }
    /**
     * Get workload summary for a user
     */
    async getWorkloadSummary(userId) {
        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) => t.status !== 'done' && t.status !== 'cancelled');
            const completedTasks = tasks.filter((t) => t.status === 'done');
            const overdueTasks = activeTasks.filter((t) => t.due_date && new Date(t.due_date) < now);
            // Calculate total workload points
            const totalWorkloadPoints = activeTasks.reduce((sum, task) => sum + (task.workload_points || 1), 0);
            // Calculate average completion time
            let averageCompletionTime;
            if (completedTasks.length > 0) {
                const completionTimes = completedTasks
                    .filter((t) => t.assigned_at && t.completed_at)
                    .map((t) => {
                    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) => t.due_date)
                .sort((a, b) => new Date(a.due_date).getTime() - new Date(b.due_date).getTime())
                .slice(0, 5)
                .map((t) => ({
                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) {
            throw new Error(`Failed to get workload summary: ${error.message}`);
        }
    }
    /**
     * Get assignment recommendations for a task
     */
    async getAssignmentRecommendations(taskId, options) {
        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;
            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 = [];
            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) {
            throw new Error(`Failed to get assignment recommendations: ${error.message}`);
        }
    }
    /**
     * Batch assign multiple tasks
     */
    async batchAssignTasks(assignments, assignerId) {
        const results = [];
        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, balancerId) {
        if (!this.client)
            await this.initializeClient();
        try {
            // Check permission
            const canAssign = await rbac_service_1.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 = [];
            // 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) {
            throw new Error(`Failed to auto-balance workload: ${error.message}`);
        }
    }
    /**
     * Get project team members
     */
    async getProjectTeamMembers(projectId) {
        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 [];
        }
    }
}
exports.AssignmentService = AssignmentService;
// Export singleton instance
exports.assignmentService = new AssignmentService();
//# sourceMappingURL=assignment.service.js.map