/**
 * Auto-Assignment Service
 *
 * Automatically assigns tasks to idle agents based on role matching,
 * priority, and dependency rules.
 *
 * @module services/autonomous/auto-assign.service
 */
import * as path from 'path';
import * as fs from 'fs/promises';
import { existsSync } from 'fs';
import { EventEmitter } from 'events';
import { parse as parseYAML } from 'yaml';
import { LoggerService } from '../core/logger.service.js';
import { TaskService } from '../project/task.service.js';
import { TaskTrackingService } from '../project/task-tracking.service.js';
import { AUTO_ASSIGN_CONSTANTS, DEFAULT_AUTO_ASSIGN_CONFIG, DEFAULT_ASSIGNMENT_STRATEGY, } from '../../types/auto-assign.types.js';
/**
 * Service for automatic task assignment to agents
 *
 * @example
 * ```typescript
 * const service = AutoAssignService.getInstance();
 * await service.initialize('/path/to/project');
 *
 * // Assign next task to an idle agent
 * const result = await service.assignNextTask('agent-session');
 * if (result) {
 *   console.log(`Assigned task ${result.taskId} to agent`);
 * }
 * ```
 */
export class AutoAssignService extends EventEmitter {
    static instance = null;
    logger;
    taskService = null;
    taskTrackingService = null;
    /** Configuration cache per project */
    configs = new Map();
    /** Task queue cache per project */
    queues = new Map();
    /** Paused projects */
    paused = new Set();
    /** Agent to project mapping */
    agentProjects = new Map();
    /** Assignment history per project */
    assignmentHistory = new Map();
    /** Daily assignment counts per agent */
    dailyAssignments = new Map();
    /** Last assignment time per agent */
    lastAssignmentTime = new Map();
    /**
     * Private constructor for singleton pattern
     */
    constructor() {
        super();
        this.logger = LoggerService.getInstance().createComponentLogger('AutoAssignService');
    }
    /**
     * Gets the singleton instance
     *
     * @returns The AutoAssignService instance
     */
    static getInstance() {
        if (!AutoAssignService.instance) {
            AutoAssignService.instance = new AutoAssignService();
        }
        return AutoAssignService.instance;
    }
    /**
     * Clears the singleton instance (for testing)
     */
    static clearInstance() {
        AutoAssignService.instance = null;
    }
    /**
     * Set the task service (for dependency injection/testing)
     *
     * @param service - Task service instance
     */
    setTaskService(service) {
        this.taskService = service;
    }
    /**
     * Set the task tracking service (for dependency injection/testing)
     *
     * @param service - Task tracking service instance
     */
    setTaskTrackingService(service) {
        this.taskTrackingService = service;
    }
    /**
     * Get the task service, creating one if needed
     *
     * @param projectPath - Project path
     * @returns Task service
     */
    getTaskService(projectPath) {
        if (!this.taskService) {
            this.taskService = new TaskService(projectPath);
        }
        return this.taskService;
    }
    /**
     * Get the task tracking service, creating one if needed
     *
     * @returns Task tracking service
     */
    getTaskTrackingService() {
        if (!this.taskTrackingService) {
            this.taskTrackingService = new TaskTrackingService();
        }
        return this.taskTrackingService;
    }
    /**
     * Initialize the service for a project
     *
     * @param projectPath - Path to the project
     */
    async initialize(projectPath) {
        // Load or create config
        const config = await this.loadConfig(projectPath);
        this.configs.set(projectPath, config);
        // Build initial queue
        await this.refreshQueue(projectPath);
        // Subscribe to task events
        this.subscribeToEvents(projectPath);
        this.logger.info('AutoAssignService initialized', { projectPath });
    }
    /**
     * Load configuration from file or return defaults
     *
     * @param projectPath - Project path
     * @returns Auto-assign configuration
     */
    async loadConfig(projectPath) {
        const configPath = path.join(projectPath, AUTO_ASSIGN_CONSTANTS.PATHS.CONFIG_DIR, AUTO_ASSIGN_CONSTANTS.PATHS.CONFIG_FILE);
        try {
            if (existsSync(configPath)) {
                const content = await fs.readFile(configPath, 'utf-8');
                const parsed = parseYAML(content);
                // Merge with defaults
                return {
                    enabled: parsed.enabled ?? DEFAULT_AUTO_ASSIGN_CONFIG.enabled,
                    strategy: {
                        ...DEFAULT_ASSIGNMENT_STRATEGY,
                        ...parsed.strategy,
                        roleMatching: parsed.strategy?.roleMatching ?? DEFAULT_ASSIGNMENT_STRATEGY.roleMatching,
                        loadBalancing: {
                            ...DEFAULT_ASSIGNMENT_STRATEGY.loadBalancing,
                            ...parsed.strategy?.loadBalancing,
                        },
                        dependencies: {
                            ...DEFAULT_ASSIGNMENT_STRATEGY.dependencies,
                            ...parsed.strategy?.dependencies,
                        },
                    },
                    notifications: {
                        ...DEFAULT_AUTO_ASSIGN_CONFIG.notifications,
                        ...parsed.notifications,
                    },
                    limits: {
                        ...DEFAULT_AUTO_ASSIGN_CONFIG.limits,
                        ...parsed.limits,
                    },
                };
            }
        }
        catch (error) {
            this.logger.warn('Failed to load config, using defaults', { projectPath, error });
        }
        return { ...DEFAULT_AUTO_ASSIGN_CONFIG };
    }
    /**
     * Get the assignment strategy configuration for a project
     *
     * @param projectPath - Path to the project
     * @returns Assignment strategy configuration
     */
    async getConfig(projectPath) {
        if (!this.configs.has(projectPath)) {
            const config = await this.loadConfig(projectPath);
            this.configs.set(projectPath, config);
        }
        return this.configs.get(projectPath).strategy;
    }
    /**
     * Update the assignment strategy configuration
     *
     * @param projectPath - Path to the project
     * @param config - Partial configuration to merge
     */
    async setConfig(projectPath, config) {
        const existing = this.configs.get(projectPath) || { ...DEFAULT_AUTO_ASSIGN_CONFIG };
        existing.strategy = {
            ...existing.strategy,
            ...config,
        };
        this.configs.set(projectPath, existing);
        this.logger.info('Config updated', { projectPath });
    }
    /**
     * Assign the next available task to an agent
     *
     * @param sessionName - Agent session name
     * @returns Assignment result or null if no task available
     */
    async assignNextTask(sessionName) {
        const projectPath = this.agentProjects.get(sessionName);
        if (!projectPath) {
            this.logger.warn('No project associated with agent', { sessionName });
            return null;
        }
        // Check if auto-assign is enabled and not paused
        if (!(await this.isAutoAssignEnabled(projectPath))) {
            this.logger.debug('Auto-assign disabled or paused', { projectPath });
            return null;
        }
        // Check agent workload
        const workload = await this.getAgentWorkload(sessionName);
        const config = await this.getConfig(projectPath);
        if (workload.currentTasks.length >= config.loadBalancing.maxConcurrentTasks) {
            this.logger.debug('Agent at max capacity', { sessionName, currentTasks: workload.currentTasks.length });
            return null;
        }
        // Check cooldown
        const lastTime = this.lastAssignmentTime.get(sessionName) || 0;
        const fullConfig = this.configs.get(projectPath) || DEFAULT_AUTO_ASSIGN_CONFIG;
        const cooldownMs = fullConfig.limits.cooldownBetweenTasks * 1000;
        if (Date.now() - lastTime < cooldownMs) {
            this.logger.debug('Agent in cooldown period', { sessionName });
            return null;
        }
        // Check daily limit
        const dailyRecord = this.dailyAssignments.get(sessionName);
        const today = new Date().toISOString().split('T')[0];
        if (dailyRecord && dailyRecord.date === today) {
            if (dailyRecord.count >= fullConfig.limits.maxAssignmentsPerDay) {
                this.logger.debug('Agent at daily limit', { sessionName, count: dailyRecord.count });
                return null;
            }
        }
        // Find next task
        const result = await this.findNextTask({
            sessionName,
            role: workload.role,
            projectPath,
        });
        if (!result.found || !result.task) {
            this.emitEvent({
                type: 'no_tasks',
                agentId: workload.agentId,
                sessionName,
                timestamp: new Date().toISOString(),
                metadata: { reason: result.reason },
            });
            return null;
        }
        // Assign the task
        return this.assignTask(result.task.taskPath, sessionName);
    }
    /**
     * Find the next task suitable for a role
     *
     * @param params - Search parameters
     * @returns Search result with task or reason for no match
     */
    async findNextTask(params) {
        const { role, projectPath, preferredTaskTypes } = params;
        const queue = await this.getTaskQueue(projectPath);
        const config = await this.getConfig(projectPath);
        // Filter tasks by eligibility
        const eligibleTasks = queue.tasks.filter((task) => {
            // Check role match
            if (task.requiredRole && task.requiredRole !== role) {
                if (!this.canRoleHandleTask(role, task, config)) {
                    return false;
                }
            }
            // Check if task type matches role capabilities
            if (task.taskType && !this.canRoleHandleTaskType(role, task.taskType, config)) {
                // Check if it's an exclusive task type for another role
                if (this.isExclusiveForOtherRole(role, task.taskType, config)) {
                    return false;
                }
            }
            // Check dependencies
            if (config.dependencies.respectBlocking && task.blockedBy.length > 0) {
                return false;
            }
            return true;
        });
        if (eligibleTasks.length === 0) {
            // Determine reason
            if (queue.tasks.length === 0) {
                return { found: false, reason: 'no_tasks' };
            }
            const blockedCount = queue.tasks.filter((t) => t.blockedBy.length > 0).length;
            if (blockedCount === queue.tasks.length) {
                return { found: false, reason: 'all_blocked' };
            }
            return { found: false, reason: 'role_mismatch' };
        }
        // Sort by priority and preference
        const sorted = this.sortTasks(eligibleTasks, config, preferredTaskTypes);
        return {
            found: true,
            task: sorted[0],
        };
    }
    /**
     * Sort tasks by priority and preferences
     *
     * @param tasks - Tasks to sort
     * @param config - Assignment strategy
     * @param preferredTypes - Preferred task types
     * @returns Sorted tasks
     */
    sortTasks(tasks, config, preferredTypes) {
        return [...tasks].sort((a, b) => {
            // Preferred types first
            if (preferredTypes && preferredTypes.length > 0) {
                const aPreferred = a.taskType && preferredTypes.includes(a.taskType);
                const bPreferred = b.taskType && preferredTypes.includes(b.taskType);
                if (aPreferred && !bPreferred)
                    return -1;
                if (!aPreferred && bPreferred)
                    return 1;
            }
            // Then by prioritization strategy
            if (config.prioritization === 'priority') {
                // Lower priority number = more important (CRITICAL=1 is highest priority)
                return a.priority - b.priority;
            }
            else if (config.prioritization === 'fifo') {
                return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
            }
            else if (config.prioritization === 'deadline') {
                // Tasks with estimated hours should be done sooner
                const aHours = a.estimatedHours ?? Infinity;
                const bHours = b.estimatedHours ?? Infinity;
                return aHours - bHours;
            }
            return 0;
        });
    }
    /**
     * Check if a role can handle a specific task
     *
     * @param role - Agent role
     * @param task - Task to check
     * @param config - Assignment strategy
     * @returns True if role can handle the task
     */
    canRoleHandleTask(role, task, config) {
        // If task requires a specific role and it doesn't match, check if the role can substitute
        if (task.requiredRole && task.requiredRole !== role) {
            // Check if the current role can substitute for the required role
            // For example, 'developer' can do generic dev work, but not 'qa' tasks
            const roleHierarchy = {
                'frontend-developer': ['developer'],
                'backend-developer': ['developer'],
                developer: [],
                pm: [],
                tpm: [],
                pgm: [],
                qa: ['tester'],
                tester: [],
                orchestrator: [],
                designer: [],
                devops: [],
            };
            // Check if the agent's role is in the hierarchy chain for required role
            const canSubstitute = roleHierarchy[role]?.includes(task.requiredRole) || false;
            if (!canSubstitute) {
                return false;
            }
        }
        // Check task type compatibility if present
        const rule = config.roleMatching.find((r) => r.role === role);
        if (!rule)
            return false;
        if (task.taskType && !rule.taskTypes.includes(task.taskType)) {
            return false;
        }
        return true;
    }
    /**
     * Check if a role can handle a task type
     *
     * @param role - Agent role
     * @param taskType - Task type
     * @param config - Assignment strategy
     * @returns True if role can handle the task type
     */
    canRoleHandleTaskType(role, taskType, config) {
        const rule = config.roleMatching.find((r) => r.role === role);
        if (!rule)
            return false;
        return rule.taskTypes.includes(taskType);
    }
    /**
     * Check if a task type is exclusive to another role
     *
     * @param role - Current role
     * @param taskType - Task type
     * @param config - Assignment strategy
     * @returns True if exclusive to another role
     */
    isExclusiveForOtherRole(role, taskType, config) {
        for (const rule of config.roleMatching) {
            if (rule.role !== role && rule.exclusive && rule.taskTypes.includes(taskType)) {
                return true;
            }
        }
        return false;
    }
    /**
     * Assign a specific task to an agent
     *
     * @param taskPath - Path to the task file
     * @param sessionName - Agent session name
     * @returns Assignment result
     */
    async assignTask(taskPath, sessionName) {
        const workload = await this.getAgentWorkload(sessionName);
        const projectPath = this.agentProjects.get(sessionName);
        if (!projectPath) {
            throw new Error(`No project associated with session: ${sessionName}`);
        }
        // Get task info
        const taskService = this.getTaskService(projectPath);
        const tasks = await taskService.getAllTasks();
        const task = tasks.find((t) => t.filePath === taskPath);
        if (!task) {
            throw new Error(`Task not found: ${taskPath}`);
        }
        const now = new Date().toISOString();
        // Create assignment record
        const assignment = {
            taskId: task.id,
            agentId: workload.agentId,
            sessionName,
            assignedAt: now,
            status: 'active',
        };
        // Add to assignment history
        const history = this.assignmentHistory.get(projectPath) || [];
        history.push(assignment);
        this.assignmentHistory.set(projectPath, history);
        // Update daily count
        const today = now.split('T')[0];
        const dailyRecord = this.dailyAssignments.get(sessionName);
        if (dailyRecord && dailyRecord.date === today) {
            dailyRecord.count++;
        }
        else {
            this.dailyAssignments.set(sessionName, { date: today, count: 1 });
        }
        // Update last assignment time
        this.lastAssignmentTime.set(sessionName, Date.now());
        const result = {
            taskId: task.id,
            taskPath,
            agentId: workload.agentId,
            sessionName,
            assignedAt: now,
        };
        // Emit event
        this.emitEvent({
            type: 'task_assigned',
            agentId: workload.agentId,
            sessionName,
            taskId: task.id,
            timestamp: now,
            metadata: {
                taskPath,
                taskTitle: task.title,
                priority: task.priority,
            },
        });
        // Refresh queue to reflect assignment
        await this.refreshQueue(projectPath);
        this.logger.info('Task assigned', {
            taskId: task.id,
            sessionName,
            taskTitle: task.title,
        });
        return result;
    }
    /**
     * Get the task queue for a project
     *
     * @param projectPath - Path to the project
     * @returns Task queue
     */
    async getTaskQueue(projectPath) {
        if (!this.queues.has(projectPath)) {
            await this.refreshQueue(projectPath);
        }
        return this.queues.get(projectPath);
    }
    /**
     * Refresh the task queue from the filesystem
     *
     * @param projectPath - Path to the project
     */
    async refreshQueue(projectPath) {
        const taskService = this.getTaskService(projectPath);
        const allTasks = await taskService.getAllTasks();
        // Get open tasks
        const openTasks = allTasks.filter((t) => t.status === 'open');
        // Get in-progress task IDs to check dependencies
        const inProgressTasks = allTasks.filter((t) => t.status === 'in_progress');
        const inProgressIds = new Set(inProgressTasks.map((t) => t.id));
        const openIds = new Set(openTasks.map((t) => t.id));
        // Build queue
        const queuedTasks = openTasks.map((task) => {
            // Parse dependencies from task content if available
            const dependencies = this.extractDependencies(task);
            // Calculate blocked by - tasks that are dependencies and not yet done
            const blockedBy = dependencies.filter((d) => inProgressIds.has(d) || openIds.has(d));
            return {
                taskId: task.id,
                taskPath: task.filePath,
                title: task.title,
                priority: this.getPriorityValue(task.priority),
                taskType: this.extractTaskType(task),
                requiredRole: task.assignee,
                dependencies,
                blockedBy,
                createdAt: task.createdAt,
                estimatedHours: this.extractEstimatedHours(task),
                labels: this.extractLabels(task),
            };
        });
        // Get assignment history
        const assignments = this.assignmentHistory.get(projectPath) || [];
        const config = await this.getConfig(projectPath);
        this.queues.set(projectPath, {
            projectPath,
            tasks: queuedTasks,
            assignments,
            config,
            lastUpdated: new Date().toISOString(),
        });
        this.logger.debug('Queue refreshed', {
            projectPath,
            taskCount: queuedTasks.length,
        });
    }
    /**
     * Convert priority string to numeric value
     *
     * @param priority - Priority string
     * @returns Numeric priority value
     */
    getPriorityValue(priority) {
        const values = {
            critical: AUTO_ASSIGN_CONSTANTS.PRIORITY.CRITICAL,
            high: AUTO_ASSIGN_CONSTANTS.PRIORITY.HIGH,
            medium: AUTO_ASSIGN_CONSTANTS.PRIORITY.MEDIUM,
            low: AUTO_ASSIGN_CONSTANTS.PRIORITY.LOW,
            backlog: AUTO_ASSIGN_CONSTANTS.PRIORITY.BACKLOG,
        };
        return values[priority.toLowerCase()] ?? AUTO_ASSIGN_CONSTANTS.PRIORITY.MEDIUM;
    }
    /**
     * Extract task type from task
     *
     * @param task - Task object
     * @returns Task type or undefined
     */
    extractTaskType(task) {
        // Try to infer from title or labels
        const title = task.title.toLowerCase();
        const taskTypes = Object.values(AUTO_ASSIGN_CONSTANTS.TASK_TYPES).flat();
        for (const type of taskTypes) {
            if (title.includes(type)) {
                return type;
            }
        }
        return undefined;
    }
    /**
     * Extract dependencies from task
     *
     * @param task - Task object
     * @returns Array of dependency task IDs
     */
    extractDependencies(task) {
        // Look for dependency patterns in description
        const depPattern = /depends on:?\s*([^\n]+)/i;
        const match = task.description.match(depPattern);
        if (match) {
            return match[1]
                .split(/[,\s]+/)
                .map((d) => d.trim())
                .filter((d) => d.length > 0);
        }
        return [];
    }
    /**
     * Extract estimated hours from task
     *
     * @param task - Task object
     * @returns Estimated hours or undefined
     */
    extractEstimatedHours(task) {
        const estPattern = /estimated:?\s*(\d+)\s*(hours?|h)/i;
        const match = task.description.match(estPattern);
        if (match) {
            return parseInt(match[1], 10);
        }
        return undefined;
    }
    /**
     * Extract labels from task
     *
     * @param task - Task object
     * @returns Array of labels
     */
    extractLabels(task) {
        // Look for label patterns
        const labelPattern = /labels?:?\s*([^\n]+)/i;
        const match = task.description.match(labelPattern);
        if (match) {
            return match[1]
                .split(/[,\s]+/)
                .map((l) => l.trim())
                .filter((l) => l.length > 0);
        }
        return [];
    }
    /**
     * Get an agent's current workload
     *
     * @param sessionName - Agent session name
     * @returns Agent workload information
     */
    async getAgentWorkload(sessionName) {
        const trackingService = this.getTaskTrackingService();
        const tasks = await trackingService.getAllInProgressTasks() || [];
        // Filter tasks for this agent
        const agentTasks = tasks.filter((t) => t.assignedSessionName === sessionName &&
            (t.status === 'assigned' || t.status === 'active'));
        // Calculate completed today
        const today = new Date().toISOString().split('T')[0];
        const dailyRecord = this.dailyAssignments.get(sessionName);
        const completedToday = dailyRecord?.date === today ? dailyRecord.count : 0;
        return {
            sessionName,
            agentId: agentTasks[0]?.assignedTeamMemberId || sessionName,
            role: this.getAgentRole(sessionName),
            currentTasks: agentTasks.map((t) => t.id),
            completedToday,
            averageIterations: 0, // Would need iteration tracking to calculate
        };
    }
    /**
     * Get the role for an agent session
     *
     * @param sessionName - Session name
     * @returns Role string
     */
    getAgentRole(sessionName) {
        // Parse role from session name convention (e.g., "project-developer-1")
        const parts = sessionName.split('-');
        if (parts.length >= 2) {
            // Common role patterns
            const roleKeywords = [
                'developer',
                'frontend-developer',
                'backend-developer',
                'qa',
                'tester',
                'pm',
                'tpm',
                'pgm',
                'designer',
                'orchestrator',
            ];
            for (const keyword of roleKeywords) {
                if (sessionName.includes(keyword)) {
                    return keyword;
                }
            }
        }
        return 'developer'; // Default role
    }
    /**
     * Register an agent with a project
     *
     * @param sessionName - Agent session name
     * @param projectPath - Project path
     */
    registerAgent(sessionName, projectPath) {
        this.agentProjects.set(sessionName, projectPath);
        this.logger.debug('Agent registered', { sessionName, projectPath });
    }
    /**
     * Unregister an agent
     *
     * @param sessionName - Agent session name
     */
    unregisterAgent(sessionName) {
        this.agentProjects.delete(sessionName);
        this.logger.debug('Agent unregistered', { sessionName });
    }
    /**
     * Emit an assignment event
     *
     * @param event - Event to emit
     */
    emitEvent(event) {
        this.emit(event.type, event);
        this.emit('assignment_event', event);
        this.logger.debug('Event emitted', { type: event.type, taskId: event.taskId });
    }
    /**
     * Register a handler for task assignment events
     *
     * @param handler - Event handler function
     */
    onTaskAssigned(handler) {
        this.on('task_assigned', handler);
    }
    /**
     * Register a handler for agent idle events
     *
     * @param handler - Event handler function
     */
    onAgentIdle(handler) {
        this.on('agent_idle', handler);
    }
    /**
     * Subscribe to task tracking events for a project
     *
     * @param projectPath - Project path
     */
    subscribeToEvents(projectPath) {
        const trackingService = this.getTaskTrackingService();
        // Listen for task completions
        trackingService.on('task_completed', async (task) => {
            const sessionName = task.assignedSessionName;
            const agentProjectPath = this.agentProjects.get(sessionName);
            if (agentProjectPath === projectPath) {
                // Update assignment history
                const history = this.assignmentHistory.get(projectPath) || [];
                const assignment = history.find((a) => a.taskId === task.id && a.status === 'active');
                if (assignment) {
                    assignment.status = 'completed';
                    assignment.completedAt = new Date().toISOString();
                }
                // Emit completion event
                this.emitEvent({
                    type: 'task_completed',
                    agentId: task.assignedTeamMemberId,
                    sessionName,
                    taskId: task.id,
                    timestamp: new Date().toISOString(),
                });
                // Try to assign next task
                await this.assignNextTask(sessionName);
            }
        });
    }
    /**
     * Pause auto-assignment for a project
     *
     * @param projectPath - Path to the project
     */
    async pauseAutoAssign(projectPath) {
        this.paused.add(projectPath);
        this.logger.info('Auto-assign paused', { projectPath });
    }
    /**
     * Resume auto-assignment for a project
     *
     * @param projectPath - Path to the project
     */
    async resumeAutoAssign(projectPath) {
        this.paused.delete(projectPath);
        this.logger.info('Auto-assign resumed', { projectPath });
    }
    /**
     * Check if auto-assignment is enabled for a project
     *
     * @param projectPath - Path to the project
     * @returns True if enabled and not paused
     */
    async isAutoAssignEnabled(projectPath) {
        if (this.paused.has(projectPath)) {
            return false;
        }
        const config = this.configs.get(projectPath);
        return config?.enabled ?? false;
    }
    /**
     * Mark a task as failed
     *
     * @param taskId - Task ID
     * @param sessionName - Agent session name
     * @param reason - Failure reason
     */
    async markTaskFailed(taskId, sessionName, reason) {
        const projectPath = this.agentProjects.get(sessionName);
        if (projectPath) {
            const history = this.assignmentHistory.get(projectPath) || [];
            const assignment = history.find((a) => a.taskId === taskId && a.status === 'active');
            if (assignment) {
                assignment.status = 'failed';
            }
            this.emitEvent({
                type: 'task_failed',
                agentId: assignment?.agentId || sessionName,
                sessionName,
                taskId,
                timestamp: new Date().toISOString(),
                metadata: { reason },
            });
        }
    }
    /**
     * Get assignment statistics for a project
     *
     * @param projectPath - Project path
     * @returns Assignment statistics
     */
    getStatistics(projectPath) {
        const history = this.assignmentHistory.get(projectPath) || [];
        return {
            totalAssigned: history.length,
            completed: history.filter((a) => a.status === 'completed').length,
            failed: history.filter((a) => a.status === 'failed').length,
            active: history.filter((a) => a.status === 'active').length,
        };
    }
    /**
     * Clear all cached data (for testing)
     */
    clearCache() {
        this.configs.clear();
        this.queues.clear();
        this.paused.clear();
        this.agentProjects.clear();
        this.assignmentHistory.clear();
        this.dailyAssignments.clear();
        this.lastAssignmentTime.clear();
    }
}
//# sourceMappingURL=auto-assign.service.js.map