/**
 * Generation Orchestrator
 * Coordinates code generation with checkpoint management, error handling, and retry logic
 */

import * as _path from 'path';
import { Workflow } from '../types/workflow';
import { CodeGenerator, CodeGenerationOptions, CodeGenerationResult } from './code-generator';
import { CheckpointManager, GenerationStage } from '../utils/checkpoint-manager';
import {
  EnhancedError,
  ErrorFactory,
  ErrorAggregator,
  ErrorSeverity,
  ErrorCode,
} from '../utils/error-handler';
import { RetryManager, RetryHelpers, RetryStrategy } from '../utils/retry-manager';
import { WorkflowEngine } from '../engine/workflow-engine';

export interface OrchestrationOptions extends CodeGenerationOptions {
  resume?: boolean;           // Resume from last checkpoint
  noCheckpoint?: boolean;     // Disable checkpointing
  autoRollback?: boolean;     // Auto-rollback on failure (default: true)
  retryTransient?: boolean;   // Retry transient failures (default: true)
  maxRetries?: number;        // Max retry attempts (default: 3)
  onProgress?: import('./types').ProgressCallback; // Progress callback
}

export interface OrchestrationResult extends CodeGenerationResult {
  checkpoint?: {
    stage: GenerationStage;
    percentage: number;
    canResume: boolean;
  };
  rolledBack?: boolean;
  retries?: number;
}

export class GenerationOrchestrator {
  private codeGenerator: CodeGenerator;
  private checkpointManager?: CheckpointManager;
  private errorAggregator: ErrorAggregator;
  private workflowEngine: WorkflowEngine;

  constructor() {
    this.codeGenerator = new CodeGenerator();
    this.errorAggregator = new ErrorAggregator();
    this.workflowEngine = new WorkflowEngine();
  }

  /**
   * Orchestrate code generation with full error handling and checkpointing
   */
  async generate(
    workflow: Workflow,
    options: OrchestrationOptions
  ): Promise<OrchestrationResult> {
    // Initialize checkpoint manager unless disabled
    if (!options.noCheckpoint) {
      this.checkpointManager = new CheckpointManager(options.outputDirectory);
      this.checkpointManager.initialize();
    }

    // Check if can resume from previous failure
    if (options.resume && this.checkpointManager?.canResume()) {
      return this.resumeGeneration(workflow, options);
    }

    // Execute generation with error handling
    try {
      return await this.executeGeneration(workflow, options);
    } catch (error) {
      return this.handleGenerationFailure(error, options);
    }
  }

  /**
   * Execute the full generation process
   */
  private async executeGeneration(
    workflow: Workflow,
    options: OrchestrationOptions
  ): Promise<OrchestrationResult> {
    const result: OrchestrationResult = {
      files: [],
      written: [],
      skipped: [],
      errors: [],
      warnings: [],
      retries: 0,
    };

    try {
      // Stage 1: Validation
      await this.executeStage(GenerationStage.VALIDATION, async () => {
        const validation = await this.validateWorkflow(workflow);
        if (!validation.valid) {
          validation.errors.forEach(err =>
            this.errorAggregator.add(ErrorFactory.invalidWorkflowStructure(err))
          );
          throw new Error(`Workflow validation failed: ${validation.errors.join(', ')}`);
        }
        result.warnings.push(...validation.warnings);
      });

      // Stage 2: Analysis
      await this.executeStage(GenerationStage.ANALYSIS, async () => {
        const _executionPlan = this.workflowEngine.analyze(workflow);

        // The analyze method will throw an error if there are issues
        // such as circular dependencies or invalid references
        // No need to check explicitly here
      });

      // Stage 3: Infrastructure
      await this.executeStage(GenerationStage.INFRASTRUCTURE, async () => {
        // Infrastructure generation happens within CodeGenerator
        // Just track the checkpoint here
      });

      // Stage 4: Component Generation
      await this.executeStage(GenerationStage.COMPONENT_GENERATION, async () => {
        const componentNames = Object.keys(workflow.stack);
        const succeededComponents: string[] = [];
        const failedComponents: string[] = [];
        const componentErrors: Array<{ component: string; error: string }> = [];

        for (const componentName of componentNames) {
          try {
            // Create checkpoint per component
            if (this.checkpointManager) {
              this.checkpointManager.createCheckpoint(
                GenerationStage.COMPONENT_GENERATION,
                componentName
              );
            }

            // Generate component (handled by CodeGenerator)
            // We'll track success/failure here
            succeededComponents.push(componentName);
          } catch (error) {
            failedComponents.push(componentName);
            componentErrors.push({
              component: componentName,
              error: error instanceof Error ? error.message : String(error),
            });

            // Don't throw immediately - collect all errors
          }
        }

        // If some components failed, create partial generation error
        if (failedComponents.length > 0) {
          const partialError = ErrorFactory.partialGeneration(
            succeededComponents,
            failedComponents,
            componentErrors
          );

          // Check if can recover automatically
          if (partialError.isRecoverable()) {
            result.warnings.push(
              `Partial generation: ${failedComponents.length} components failed`
            );
          } else {
            throw partialError;
          }
        }
      });

      // Stage 5: Configuration
      await this.executeStage(GenerationStage.CONFIGURATION, async () => {
        // Docker, CI/CD, etc. generation
        // Handled by CodeGenerator
      });

      // Stage 6: Finalization
      await this.executeStage(GenerationStage.FINALIZATION, async () => {
        // Manifest generation
        // Handled by CodeGenerator
      });

      // Execute actual code generation with retry logic
      const genResult = await this.executeWithRetry(
        () => this.codeGenerator.generate(workflow, options),
        options
      );

      // Merge results
      result.files = genResult.files;
      result.written = genResult.written;
      result.skipped = genResult.skipped;
      result.errors = genResult.errors;
      result.warnings.push(...genResult.warnings);

      // Track generated files in checkpoint
      if (this.checkpointManager) {
        for (const file of genResult.written) {
          this.checkpointManager.recordFileCreated(file);
        }
      }

      // Stage 7: Complete
      if (this.checkpointManager) {
        this.checkpointManager.createCheckpoint(GenerationStage.COMPLETE);
      }

      // Add checkpoint info to result
      if (this.checkpointManager) {
        const progress = this.checkpointManager.getProgress();
        result.checkpoint = {
          stage: progress.stage,
          percentage: progress.percentage,
          canResume: this.checkpointManager.canResume(),
        };

        // Clean up checkpoints after successful generation
        if (!options.dryRun) {
          this.checkpointManager.cleanup();
        }
      }

      return result;

    } catch (error) {
      // Handle generation failure
      if (error instanceof EnhancedError) {
        result.errors.push(error.format());
      } else {
        result.errors.push(error instanceof Error ? error.message : String(error));
      }

      // Auto-rollback if enabled
      if (options.autoRollback !== false && this.checkpointManager) {
        const rollbackResult = this.checkpointManager.rollback();
        if (rollbackResult.success) {
          result.rolledBack = true;
          result.warnings.push(
            `Rolled back to last checkpoint: ${rollbackResult.filesRemoved?.length || 0} files removed`
          );
        }
      }

      throw error;
    }
  }

  /**
   * Execute a generation stage with checkpoint tracking
   */
  private async executeStage(
    stage: GenerationStage,
    action: () => Promise<void>
  ): Promise<void> {
    // Create checkpoint
    if (this.checkpointManager) {
      this.checkpointManager.createCheckpoint(stage);
    }

    // Execute stage
    try {
      await action();
    } catch (error) {
      // Enhance error with stage context
      if (error instanceof Error) {
        error.message = `[${stage}] ${error.message}`;
      }
      throw error;
    }
  }

  /**
   * Execute generation with retry logic for transient failures
   */
  private async executeWithRetry(
    action: () => Promise<CodeGenerationResult>,
    options: OrchestrationOptions
  ): Promise<CodeGenerationResult> {
    if (options.retryTransient === false) {
      return action();
    }

    const result = await RetryManager.execute(action, {
      maxAttempts: options.maxRetries || 3,
      strategy: RetryStrategy.EXPONENTIAL_BACKOFF,
      initialDelay: 1000,
      maxDelay: 10000,
      onRetry: (attempt, error) => {
        console.log(`  Retrying generation (attempt ${attempt}): ${error.message}`);
      },
      shouldRetry: (error) => {
        // Don't retry validation or permanent errors
        if (error.message.includes('validation')) return false;
        if (error.message.includes('invalid')) return false;
        return true;
      },
    });

    if (!result.success) {
      throw result.error || new Error('Generation failed after retries');
    }

    return result.result!;
  }

  /**
   * Validate workflow before generation
   */
  private async validateWorkflow(workflow: Workflow): Promise<{
    valid: boolean;
    errors: string[];
    warnings: string[];
  }> {
    return RetryHelpers.retryFileOperation(
      async () => this.workflowEngine.validate(workflow),
      'validate workflow'
    );
  }

  /**
   * Resume generation from last checkpoint
   */
  private async resumeGeneration(
    workflow: Workflow,
    options: OrchestrationOptions
  ): Promise<OrchestrationResult> {
    if (!this.checkpointManager) {
      throw new Error('Cannot resume: checkpoint manager not initialized');
    }

    const resumePoint = this.checkpointManager.getResumePoint();
    if (!resumePoint) {
      throw new Error('Cannot resume: no valid resume point found');
    }

    console.log(`\n📊 Resuming from checkpoint: ${resumePoint.stage}`);
    if (resumePoint.component) {
      console.log(`   Component: ${resumePoint.component}`);
    }

    // Show progress
    const progress = this.checkpointManager.getProgress();
    console.log(`   Progress: ${progress.percentage}%`);
    console.log(`   Completed: ${progress.completedStages.join(', ')}\n`);

    // Continue generation from resume point
    // For now, we'll restart the full generation
    // In a more advanced implementation, we'd skip completed stages
    return this.executeGeneration(workflow, {
      ...options,
      resume: false, // Prevent infinite recursion
    });
  }

  /**
   * Handle generation failure
   */
  private handleGenerationFailure(
    error: unknown,
    _options: OrchestrationOptions
  ): OrchestrationResult {
    const result: OrchestrationResult = {
      files: [],
      written: [],
      skipped: [],
      errors: [],
      warnings: [],
    };

    // Convert error to EnhancedError if needed
    let enhancedError: EnhancedError;
    if (error instanceof EnhancedError) {
      enhancedError = error;
    } else if (error instanceof Error) {
      enhancedError = new EnhancedError({
        code: ErrorCode.GENERATOR_FAILED,
        severity: ErrorSeverity.ERROR,
        message: error.message,
        details: error.stack,
      });
    } else {
      enhancedError = new EnhancedError({
        code: ErrorCode.GENERATOR_FAILED,
        severity: ErrorSeverity.ERROR,
        message: String(error),
      });
    }

    result.errors.push(enhancedError.format());

    // Show checkpoint summary if available
    if (this.checkpointManager) {
      const summary = this.checkpointManager.getSummary();
      result.warnings.push(summary);

      // Add checkpoint info
      const progress = this.checkpointManager.getProgress();
      result.checkpoint = {
        stage: progress.stage,
        percentage: progress.percentage,
        canResume: this.checkpointManager.canResume(),
      };
    }

    return result;
  }

  /**
   * Get checkpoint manager (for testing)
   */
  getCheckpointManager(): CheckpointManager | undefined {
    return this.checkpointManager;
  }

  /**
   * Get error aggregator (for testing)
   */
  getErrorAggregator(): ErrorAggregator {
    return this.errorAggregator;
  }
}
