/**
 * @fileoverview AutoContextOrchestrator - Main orchestrator for CLI auto-detection
 * @module morph-spec/orchestrator
 */

import chalk from 'chalk';
import { ProjectScanner } from '../scanner/project-scanner.js';
import { ContextSanitizer } from '../sanitizer/context-sanitizer.js';
import { LLMAnalyzer } from '../llm/analyzer.js';
import { ConfigGenerator } from '../generator/config-generator.js';
import { UserReview } from '../ui/user-review.js';
import { InteractiveWizard } from '../ui/interactive-wizard.js';
import { FileWriter } from '../writer/file-writer.js';
import { readFile, access } from 'fs/promises';
import { join } from 'path';

/**
 * @typedef {import('../types/index.js').GeneratedConfigs} GeneratedConfigs
 */

/**
 * AutoContextOrchestrator - Orchestrates the complete auto-detection flow
 * @class
 */
export class AutoContextOrchestrator {
  constructor() {
    this.scanner = new ProjectScanner();
    this.sanitizer = new ContextSanitizer();
    this.llmAnalyzer = new LLMAnalyzer();
    this.configGenerator = new ConfigGenerator();
    this.userReview = new UserReview();
    this.wizard = new InteractiveWizard();
    this.fileWriter = new FileWriter();

    // Handle Ctrl+C gracefully
    this.setupSignalHandlers();
  }

  /**
   * Execute the complete auto-detection flow
   * @param {string} cwd - Current working directory
   * @param {Object} [options] - Orchestration options
   * @param {boolean} [options.skipReview] - Skip user review (auto-approve)
   * @param {boolean} [options.fallbackOnError] - Fallback to wizard on LLM error (default: true)
   * @param {boolean} [options.wizardMode] - Force wizard mode (skip LLM)
   * @returns {Promise<{success: boolean, configs: GeneratedConfigs|null}>}
   */
  async execute(cwd, options = {}) {
    const {
      skipReview = false,
      fallbackOnError = true,
      wizardMode = false
    } = options;

    try {
      console.log(chalk.bold.cyan('\n🔍 MORPH-SPEC Auto Context Detection\n'));

      let projectConfig;

      // Step 1: Decide between LLM or Wizard mode
      if (wizardMode) {
        console.log(chalk.yellow('  Using interactive wizard mode (--wizard flag)\n'));
        projectConfig = await this.wizard.run();
      } else {
        try {
          // Step 1a: Scan project
          console.log(chalk.dim('  [1/6] Scanning project directory...'));
          const projectContext = await this.scanner.scan(cwd);

          // Step 1b: Sanitize context
          console.log(chalk.dim('  [2/6] Sanitizing context (removing secrets)...'));
          const sanitizedContext = this.sanitizer.sanitize(projectContext);

          // Step 1c: Analyze with LLM
          console.log(chalk.dim('  [3/6] Analyzing with Claude Code LLM...'));
          projectConfig = await this.llmAnalyzer.analyze(sanitizedContext);

          console.log(chalk.green('  ✓ Auto-detection successful\n'));
        } catch (error) {
          // LLM failed
          console.log(chalk.yellow(`\n  ⚠️  Auto-detection failed: ${error.message}\n`));

          if (!fallbackOnError) {
            throw error;
          }

          // Fallback to wizard
          console.log(chalk.cyan('  Falling back to interactive wizard...\n'));
          projectConfig = await this.wizard.run();
        }
      }

      // Step 2: Generate configs
      console.log(chalk.dim('  [4/6] Generating configuration files...'));
      const configs = await this.configGenerator.generate(projectConfig);

      // Step 3: User review (unless skipped)
      let finalConfigs = configs;

      if (!skipReview) {
        console.log(chalk.dim('  [5/6] Requesting user approval...'));

        // Check if updating existing configs
        const existingConfigs = await this.readExistingConfigs(cwd);

        const approvalResponse = await this.userReview.promptForApproval(
          configs,
          projectConfig,
          existingConfigs
        );

        if (approvalResponse.action === 'cancel') {
          console.log(chalk.yellow('\n❌ Operation canceled by user\n'));
          console.log(chalk.dim(`   Reason: ${approvalResponse.cancelReason || 'User canceled'}\n`));
          return { success: false, configs: null };
        }

        if (approvalResponse.editedConfigs) {
          finalConfigs = approvalResponse.editedConfigs;
        }
      } else {
        console.log(chalk.dim('  [5/6] Skipping user review (auto-approve)'));
      }

      // Step 4: Save configs
      console.log(chalk.dim('  [6/6] Saving configuration files...'));

      // Backup existing configs if they exist
      await this.configGenerator.backupExisting(cwd);

      // Write new configs
      await this.fileWriter.save(cwd, finalConfigs);

      console.log(chalk.bold.green('🎉 Auto-detection complete!\n'));

      return { success: true, configs: finalConfigs };
    } catch (error) {
      console.log(chalk.bold.red('\n❌ Auto-detection failed\n'));
      console.log(chalk.red(`   Error: ${error.message}\n`));

      if (error.stack && process.env.DEBUG) {
        console.log(chalk.dim(error.stack));
      }

      return { success: false, configs: null };
    }
  }

  /**
   * Read existing configs (if they exist)
   * @param {string} cwd - Current working directory
   * @returns {Promise<Object|null>} Existing configs or null
   */
  async readExistingConfigs(cwd) {
    try {
      const projectMdPath = join(cwd, '.morph', 'project.md');
      const configJsonPath = join(cwd, '.morph', 'config', 'config.json');

      const [projectMdExists, configJsonExists] = await Promise.all([
        this.fileExists(projectMdPath),
        this.fileExists(configJsonPath)
      ]);

      if (!projectMdExists && !configJsonExists) {
        return null; // No existing configs
      }

      const [projectMd, configJson] = await Promise.all([
        projectMdExists ? readFile(projectMdPath, 'utf-8') : null,
        configJsonExists ? readFile(configJsonPath, 'utf-8') : null
      ]);

      return { projectMd, configJson };
    } catch (error) {
      return null; // Error reading configs, treat as non-existent
    }
  }

  /**
   * Check if file exists
   * @param {string} filepath - File path
   * @returns {Promise<boolean>}
   */
  async fileExists(filepath) {
    try {
      await access(filepath);
      return true;
    } catch {
      return false;
    }
  }

  /**
   * Setup signal handlers for graceful shutdown
   */
  setupSignalHandlers() {
    const handleExit = () => {
      console.log(chalk.yellow('\n\n⚠️  Operation interrupted by user (Ctrl+C)\n'));
      console.log(chalk.dim('   No files were modified\n'));
      process.exit(0);
    };

    process.on('SIGINT', handleExit);
    process.on('SIGTERM', handleExit);
  }
}
