import { TmuxBridge } from './tmuxBridge';
import { execSync } from 'child_process';
import * as fs from 'fs';
import { ConditionWaiter } from './conditionWaiter';
import { resourceManager } from './resourceManager';
import { circuitRegistry, CircuitState } from './circuitBreaker';
import { stateManager } from './threadSafeState';

export interface ProjectOrchestratorConfig {
	checkInterval?: number; // milliseconds
	maxRetries?: number;
	autoCreatePR?: boolean;
	credentials?: any;
}

export interface ProjectStatus {
	projectName: string;
	isComplete: boolean;
	qaApproved: boolean;
	readyForPR: boolean;
	prCreated: boolean;
	prUrl?: string;
	lastCheck: Date;
	retryCount: number;
}

/**
 * Autonomous workflow orchestrator for project completion and PR creation
 */
export class ProjectOrchestrator {
	private bridge: TmuxBridge;
	private config: ProjectOrchestratorConfig;
	private monitoredProjects = stateManager.getMap<string, ProjectStatus>('monitoredProjects');
	private monitoringResourceId?: string;
	private isMonitoring = false;

	constructor(bridge: TmuxBridge, config: ProjectOrchestratorConfig = {}) {
		this.bridge = bridge;
		this.config = {
			checkInterval: 60000, // 1 minute
			maxRetries: 5,
			autoCreatePR: true,
			...config,
		};
	}

	/**
	 * Add project to monitoring queue
	 */
	async addProject(projectName: string): Promise<void> {
		if (!this.monitoredProjects.safeHas(projectName)) {
			try {
				await this.monitoredProjects.safeSet(projectName, {
					projectName,
					isComplete: false,
					qaApproved: false,
					readyForPR: false,
					prCreated: false,
					lastCheck: new Date(),
					retryCount: 0,
				});

				console.log(`Added project ${projectName} to monitoring queue`);

				// Start monitoring if not already running
				if (!this.isMonitoring) {
					this.startMonitoring();
				}
			} catch (error) {
				console.error(`Failed to add project ${projectName} to monitoring:`, error.message);
				throw error;
			}
		}
	}

	/**
	 * Remove project from monitoring
	 */
	async removeProject(projectName: string): Promise<void> {
		try {
			const deleted = await this.monitoredProjects.safeDelete(projectName);
			if (deleted) {
				console.log(`Removed project ${projectName} from monitoring`);
			}

			// Stop monitoring if no projects left
			if (this.monitoredProjects.safeSize() === 0) {
				await this.stopMonitoring();
			}
		} catch (error) {
			console.error(`Failed to remove project ${projectName} from monitoring:`, error.message);
			throw error;
		}
	}

	/**
	 * Start autonomous monitoring
	 */
	startMonitoring(): void {
		if (this.isMonitoring) {
			return;
		}

		console.log('Starting autonomous project monitoring...');
		this.isMonitoring = true;

		const { id } = resourceManager.createInterval(async () => {
			await this.checkAllProjects();
		}, this.config.checkInterval!, 'Project orchestrator monitoring');
		
		this.monitoringResourceId = id;
	}

	/**
	 * Stop monitoring
	 */
	async stopMonitoring(): Promise<void> {
		if (this.monitoringResourceId) {
			await resourceManager.cleanup(this.monitoringResourceId);
			this.monitoringResourceId = undefined;
		}
		this.isMonitoring = false;
		console.log('Stopped autonomous project monitoring');
	}

	/**
	 * Check all monitored projects for completion
	 */
	private async checkAllProjects(): Promise<void> {
		// Circuit breaker for project monitoring
		const circuitBreaker = circuitRegistry.getBreaker('project-monitoring', {
			failureThreshold: 5,
			recoveryTimeout: 180000, // 3 minutes
			successThreshold: 3,
			monitoringWindow: 600000, // 10 minutes
			maxRetryAttempts: 2,
			onStateChange: (state, reason) => {
				console.log(`[Circuit Breaker] Project monitoring: ${state} - ${reason}`);
			},
			onFailure: (error) => {
				console.error(`[Circuit Breaker] Project monitoring failure:`, error.message);
			}
		});

		await circuitBreaker.execute(async () => {
			const promises = this.monitoredProjects.safeKeys().map(async (projectName) => {
				try {
					await this.checkProject(projectName);
				} catch (error) {
					console.error(`Error checking project ${projectName}:`, error.message);
				}
			});

			await Promise.allSettled(promises);
		}, async () => {
			// Fallback: log warning and skip this monitoring cycle
			console.warn('[Circuit Breaker] Project monitoring is in open state, skipping monitoring cycle');
			// No return value needed for void function
		});
	}

	/**
	 * Check individual project status and trigger completion workflow
	 */
	private async checkProject(projectName: string): Promise<void> {
		// Circuit breaker for individual project checking
		const circuitBreaker = circuitRegistry.getBreaker(`project-check-${projectName}`, {
			failureThreshold: 3,
			recoveryTimeout: 120000, // 2 minutes
			successThreshold: 2,
			maxRetryAttempts: 1,
			onStateChange: (state, reason) => {
				console.log(`[Circuit Breaker] Project check for ${projectName}: ${state} - ${reason}`);
			}
		});

		await circuitBreaker.execute(async () => {
			return await this.performProjectCheck(projectName);
		}, async () => {
			// Fallback: increment retry count and skip this check
			const status = this.monitoredProjects.safeGet(projectName);
			if (status) {
				try {
					await this.monitoredProjects.safeUpdate(projectName, (currentStatus) => {
						if (currentStatus) {
							currentStatus.retryCount++;
							currentStatus.lastCheck = new Date();
						}
						return currentStatus!;
					});
					console.warn(`[Circuit Breaker] Project check for ${projectName} failed, retry count: ${status.retryCount}`);
					
					// Remove from monitoring if too many failures
					if (status.retryCount >= (this.config.maxRetries || 5)) {
						console.error(`Max retries reached for ${projectName} - removing from monitoring due to circuit breaker`);
						await this.removeProject(projectName);
					}
				} catch (error) {
					console.error(`Failed to update status for ${projectName}:`, error.message);
				}
			}
			// No return value needed for void function
		});
	}

	/**
	 * Perform the actual project check (extracted for circuit breaker protection)
	 */
	private async performProjectCheck(projectName: string): Promise<void> {
		const status = this.monitoredProjects.safeGet(projectName);
		if (!status || status.prCreated) {
			return; // Already completed or not found
		}

		try {
			// Check if project session exists
			const sessions = await this.bridge.getTmuxSessions();
			const projectSession = sessions.find(s => s.name === projectName);

			if (!projectSession) {
				console.warn(`Project session ${projectName} not found - removing from monitoring`);
				await this.removeProject(projectName);
				return;
			}

			// Check completion status
			const completionStatus = await this.checkProjectCompletion(projectName);

			// Update status thread-safely
			try {
				await this.monitoredProjects.safeUpdate(projectName, (currentStatus) => {
					if (currentStatus) {
						currentStatus.isComplete = completionStatus.isComplete;
						currentStatus.qaApproved = completionStatus.qaApproved;
						currentStatus.readyForPR = completionStatus.readyForPR;
						currentStatus.lastCheck = new Date();
					}
					return currentStatus!;
				});
			} catch (error) {
				console.error(`Failed to update project status for ${projectName}:`, error.message);
				return;
			}

			// Get current status for PR checks
			const currentStatus = this.monitoredProjects.safeGet(projectName);
			if (!currentStatus) {
				return; // Status was removed during update
			}

			// If ready for PR and auto-create is enabled, create PR
			if (currentStatus.readyForPR && !currentStatus.prCreated && this.config.autoCreatePR) {
				console.log(`Project ${projectName} is ready - initiating automatic PR creation...`);
				
				const prResult = await this.createAutomaticPR(projectName);
				
				if (prResult.success) {
					try {
						await this.monitoredProjects.safeUpdate(projectName, (status) => {
							if (status) {
								status.prCreated = true;
								status.prUrl = prResult.prUrl;
							}
							return status!;
						});
						console.log(`Autonomous PR creation successful for ${projectName}: ${prResult.prUrl}`);
						
						// Notify team
						await this.notifyProjectCompletion(projectName, prResult.prUrl!);
						
						// Remove from monitoring
						await this.removeProject(projectName);
					} catch (error) {
						console.error(`Failed to update PR completion status for ${projectName}:`, error.message);
					}
				} else {
					try {
						const updatedStatus = await this.monitoredProjects.safeUpdate(projectName, (status) => {
							if (status) {
								status.retryCount++;
							}
							return status!;
						});
						console.error(`PR creation failed for ${projectName} (attempt ${updatedStatus.retryCount}): ${prResult.error}`);
						
						if (updatedStatus.retryCount >= (this.config.maxRetries || 5)) {
							console.error(`Max retries reached for ${projectName} - removing from monitoring`);
							await this.removeProject(projectName);
						}
					} catch (error) {
						console.error(`Failed to update retry count for ${projectName}:`, error.message);
					}
				}
			}
		} catch (error) {
			try {
				const updatedStatus = await this.monitoredProjects.safeUpdate(projectName, (status) => {
					if (status) {
						status.retryCount++;
					}
					return status!;
				});
				console.error(`Error monitoring project ${projectName}:`, error.message);
				
				if (updatedStatus && updatedStatus.retryCount >= (this.config.maxRetries || 5)) {
					console.error(`Max retries reached for ${projectName} - removing from monitoring`);
					await this.removeProject(projectName);
				}
			} catch (updateError) {
				console.error(`Failed to update error count for ${projectName}:`, updateError.message);
			}
		}
	}

	/**
	 * Check project completion status
	 */
	private async checkProjectCompletion(projectName: string): Promise<{
		isComplete: boolean;
		qaApproved: boolean;
		readyForPR: boolean;
	}> {
		// Circuit breaker for completion checking
		const circuitBreaker = circuitRegistry.getBreaker(`completion-check-${projectName}`, {
			failureThreshold: 3,
			recoveryTimeout: 90000, // 1.5 minutes
			successThreshold: 2,
			maxRetryAttempts: 1,
			onStateChange: (state, reason) => {
				console.log(`[Circuit Breaker] Completion check for ${projectName}: ${state} - ${reason}`);
			}
		});

		return await circuitBreaker.execute(async () => {
			return await this.performCompletionCheck(projectName);
		}, async () => {
			// Fallback: return safe status indicating not ready
			console.warn(`[Circuit Breaker] Completion check for ${projectName} failed, returning safe status`);
			return {
				isComplete: false,
				qaApproved: false,
				readyForPR: false
			};
		});
	}

	/**
	 * Perform the actual completion check (extracted for circuit breaker protection)
	 */
	private async performCompletionCheck(projectName: string): Promise<{
		isComplete: boolean;
		qaApproved: boolean;
		readyForPR: boolean;
	}> {
		// Send completion check request to Claude Code instance
		await this.bridge.sendClaudeMessage(`${projectName}:0`, 
			'AUTONOMOUS STATUS CHECK: Please respond with "PROJECT COMPLETE" if all objectives are met and ready for PR. This is an automated check.');

		// Wait for response
		await ConditionWaiter.waitForPrompt(this.bridge, projectName, 0);

		// Check Claude Code response
		const output = await this.bridge.captureWindowContent(projectName, 0, 20);
		let response = '';
		if (typeof output === 'string') {
			response = output.split('\n').slice(-10).join('\n').toLowerCase();
		}

		// Check for completion signals
		const completionSignals = [
			'project complete',
			'ready for pr',
			'ready for pull request',
			'objectives met',
			'deliverables complete',
			'implementation finished'
		];

		const isComplete = completionSignals.some(signal => response.includes(signal));

		// Since it's a single Claude Code instance, assume QA is handled internally
		// Look for quality indicators in the same response
		const qualityIndicators = [
			'tests passed',
			'validation complete',
			'quality check',
			'all tests',
			'qa complete',
			'approved'
		];

		const qaApproved = isComplete && (
			qualityIndicators.some(indicator => response.includes(indicator)) ||
			response.includes('project complete') // Implicit approval when project is complete
		);

		return {
			isComplete,
			qaApproved,
			readyForPR: isComplete && qaApproved,
		};
	}

	/**
	 * Create automatic pull request
	 */
	private async createAutomaticPR(projectName: string): Promise<{
		success: boolean;
		prUrl?: string;
		error?: string;
	}> {
		// Circuit breaker for PR creation
		const circuitBreaker = circuitRegistry.getBreaker(`pr-creation-${projectName}`, {
			failureThreshold: 2,
			recoveryTimeout: 300000, // 5 minutes
			successThreshold: 1,
			maxRetryAttempts: 1,
			onStateChange: (state, reason) => {
				console.log(`[Circuit Breaker] PR creation for ${projectName}: ${state} - ${reason}`);
			}
		});

		return await circuitBreaker.execute(async () => {
			return await this.performPRCreation(projectName);
		}, async () => {
			// Fallback: return failure status
			console.warn(`[Circuit Breaker] PR creation for ${projectName} failed, circuit breaker is open`);
			return {
				success: false,
				error: 'PR creation temporarily unavailable due to circuit breaker protection'
			};
		});
	}

	/**
	 * Perform the actual PR creation (extracted for circuit breaker protection)
	 */
	private async performPRCreation(projectName: string): Promise<{
		success: boolean;
		prUrl?: string;
		error?: string;
	}> {
		try {
			// Get project path
			await this.bridge.sendCommandToWindow(projectName, 0, 'pwd');
			await ConditionWaiter.waitForPrompt(this.bridge, projectName, 0);
			
			const output = await this.bridge.captureWindowContent(projectName, 0, 5);
			let projectPath = '';
			if (typeof output === 'string') {
				const lines = output.trim().split('\n');
				projectPath = lines[lines.length - 1].trim();
			}

			if (!projectPath) {
				throw new Error('Could not determine project path');
			}

			// Get current branch
			const branchCmd = `cd ${projectPath} && git branch --show-current`;
			await this.bridge.sendCommandToWindow(projectName, 0, branchCmd);
			await ConditionWaiter.waitForPrompt(this.bridge, projectName, 0);
			
			const branchOutput = await this.bridge.captureWindowContent(projectName, 0, 5);
			let currentBranch = '';
			if (typeof branchOutput === 'string') {
				const lines = branchOutput.trim().split('\n');
				currentBranch = lines[lines.length - 1].trim();
			}

			// Push to remote first
			const pushCmd = `cd ${projectPath} && git add . && git commit -m "Autonomous completion commit" && git push -u origin ${currentBranch}`;
			await this.bridge.sendCommandToWindow(projectName, 0, pushCmd);
			await ConditionWaiter.waitForGitOperation(this.bridge, projectName, 0, 'push');

			// Create PR using GitHub CLI
			const prTitle = `[Autonomous] ${projectName} - Project Complete`;
			const prDescription = this.generatePRDescription(projectName);

			const prResult = await this.bridge.createGitHubPR(projectPath, {
				title: prTitle,
				body: prDescription,
				base: 'main',
				head: currentBranch,
				credentials: this.config.credentials,
			});

			return prResult;
		} catch (error) {
			return {
				success: false,
				error: error.message,
			};
		}
	}

	/**
	 * Generate PR description from template
	 */
	private generatePRDescription(projectName: string): string {
		const template = this.config.credentials?.githubConfig?.prTemplate || `
## Summary
Autonomous completion of ${projectName} project.

## Changes
- Project implementation completed autonomously
- All objectives met and validated
- Quality checks passed

## Test Plan
- Automated testing completed
- Code quality validation passed
- Ready for review and merge

## Quality Status
✅ Quality Approved - All validations passed

🤖 Generated autonomously with Claude Code Tmux Orchestrator
		`.trim();

		return template
			.replace(/{project_name}/g, projectName)
			.replace(/{project_description}/g, `Autonomous completion of ${projectName}`)
			.replace(/{changes_summary}/g, 'Implementation completed autonomously')
			.replace(/{test_summary}/g, 'All tests passed, quality approved')
			.replace(/{qa_status}/g, '✅ Quality Approved');
	}

	/**
	 * Notify Claude Code instance of successful completion
	 */
	private async notifyProjectCompletion(projectName: string, prUrl: string): Promise<void> {
		try {
			const completionMessage = `
🎉 AUTONOMOUS PROJECT COMPLETION SUCCESS! 🎉

Project: ${projectName}
Pull Request: ${prUrl}

The project has been completed autonomously and is ready for final review and merge.

Next Steps:
- Review the pull request
- Merge when ready
- Deploy to production

Autonomous orchestration complete! 🚀
			`.trim();

			// Notify Claude Code instance
			await this.bridge.sendClaudeMessage(`${projectName}:0`, completionMessage);

			console.log(`Claude Code instance notified of autonomous completion for ${projectName}`);
		} catch (error) {
			console.error(`Failed to notify Claude Code instance for ${projectName}:`, error.message);
		}
	}

	/**
	 * Get current monitoring status
	 */
	getMonitoringStatus(): {
		isMonitoring: boolean;
		projectCount: number;
		projects: ProjectStatus[];
	} {
		return {
			isMonitoring: this.isMonitoring,
			projectCount: this.monitoredProjects.safeSize(),
			projects: this.monitoredProjects.safeValues(),
		};
	}

	/**
	 * Cleanup resources
	 */
	async destroy(): Promise<void> {
		await this.stopMonitoring();
		try {
			await this.monitoredProjects.safeClear();
		} catch (error) {
			console.error('Failed to clear monitored projects:', error.message);
		}
		console.log('Project orchestrator destroyed');
	}
}