/**
 * VLCode Lite — Standalone AI IDE for VL v3.7
 * No VLClaw dependency. Clean local-first architecture.
 */
import { CLIInterface } from './core/cli.js';
import { AgentOrchestrator } from './core/orchestrator.js';
import { ToolRegistry } from './core/tool-registry.js';
import { ContextManager } from './core/context-manager.js';
import { PromptAssembler } from './core/prompt-assembler.js';
import { SessionManager } from './core/session.js';
import { VLProjectContext } from './vl/project-context.js';
import { SmartContextLoader } from './vl/smart-context.js';
import { VLSymbolIndex } from './vl/symbol-index.js';
import { BlueprintContext } from './vl/blueprint-context.js';
import { ImpactAnalyzer } from './vl/impact-analyzer.js';
import { VLAutoFix } from './vl/auto-fix.js';
import { FileCache } from './vl/file-cache.js';
import { WorkflowGenerationPipeline } from './vl/workflow-generation-pipeline.js';
import { LocalWorkspace } from './engine/local-workspace.js';
import { FileWatcher } from './vl/file-watcher.js';
import { HooksManager } from './core/hooks.js';
import { extractSingleFile, mergeIntoMeta, validateMeta } from './vl/metadata-extractor.js';
import { registerAllTools, registerIntelligenceTools, loadUserTools } from './tools/index.js';
import { loadConfig, APP_VERSION } from './utils/config.js';
import { WORKFLOW_VERSION, WORKFLOW_SPEC_DOC_VERSION } from './data/versions.js';
import { registerInstance, deregisterInstance, findFreePort } from './server/helpers.js';
import chalk from 'chalk';

export class VLCodeLite {
  constructor() {
    this.config = null;
    this.cli = null;
    this.orchestrator = null;
    this.toolRegistry = null;
    this.contextManager = null;
    this.promptAssembler = null;
    this.projectContext = null;
    this.smartContext = null;
    this.symbolIndex = null;
    this.blueprintContext = null;
    this.impactAnalyzer = null;
    this.autoFix = null;
    this.fileCache = null;
    this.sessionManager = null;
    this.generationPipeline = null;
    this.localWorkspace = null;
    this.fileWatcher = null;
    this.webServer = null;
  }

  async run(args) {
    const initStart = Date.now();

    // 1. Load config
    this.config = loadConfig(args);

    // 2. CLI interface
    this.cli = new CLIInterface(this.config);

    // 3. VL project context
    this.projectContext = new VLProjectContext(this.config.workDir);
    await this.projectContext.load();

    // 4. File cache
    this.fileCache = new FileCache(this.config.workDir);
    this.config.fileCache = this.fileCache;
    if (this.projectContext.isVLProject()) {
      await this.fileCache.warmUp(this.projectContext.getAllFiles());
    }

    // 4b. Local Workspace
    this.localWorkspace = new LocalWorkspace(this.config.workspaceRoot || this.config.workDir);

    // 5. Tool registry
    this.toolRegistry = new ToolRegistry();
    this.config.toolRegistry = this.toolRegistry;
    registerAllTools(this.toolRegistry, this.config, this.projectContext, {
      localWorkspace: this.localWorkspace,
    });

    // 6. Context manager + prompt assembler
    this.contextManager = new ContextManager(this.config);
    this.promptAssembler = new PromptAssembler(this.config, this.projectContext);

    // 7. Agent orchestrator
    this.orchestrator = new AgentOrchestrator({
      config: this.config,
      toolRegistry: this.toolRegistry,
      contextManager: this.contextManager,
      promptAssembler: this.promptAssembler,
      cli: this.cli,
      projectContext: this.projectContext,
    });

    // 8. Smart Context Loader
    this.smartContext = new SmartContextLoader(this.projectContext);
    await this.smartContext.buildGraph();
    this.smartContext.contextManager = this.contextManager;
    this.orchestrator.smartContext = this.smartContext;

    // 9. VL Symbol Index
    this.symbolIndex = new VLSymbolIndex(this.projectContext);
    await this.symbolIndex.build();
    this.orchestrator.symbolIndex = this.symbolIndex;

    // 10. Blueprint Context
    this.blueprintContext = new BlueprintContext(this.projectContext);
    await this.blueprintContext.load();
    this.orchestrator.blueprintContext = this.blueprintContext;

    // 11. Impact Analyzer
    this.impactAnalyzer = new ImpactAnalyzer(this.symbolIndex, this.projectContext);

    // 12. Auto-Fix engine
    this.autoFix = new VLAutoFix(this.projectContext);

    // 13. Register intelligence tools
    registerIntelligenceTools(this.toolRegistry, this.config, {
      symbolIndex: this.symbolIndex,
      impactAnalyzer: this.impactAnalyzer,
      autoFix: this.autoFix,
      projectContext: this.projectContext,
    });

    // 13b. Load user-defined tools from ~/.vl-code/tools/
    loadUserTools(this.toolRegistry, this.config).catch(() => {});

    // 14. Generation Pipeline
    this.generationPipeline = new WorkflowGenerationPipeline(this.config);
    this.orchestrator.generationPipeline = this.generationPipeline;

    // 14b. Hooks Manager
    this.hooksManager = new HooksManager(this.config.workDir);
    if (this.hooksManager.hasHooks()) {
      this.orchestrator.hooks = this.hooksManager;
    }

    // 15. Session Manager
    this.sessionManager = new SessionManager(this.config.workDir);
    await this.sessionManager.init();
    const sessionResult = await this.sessionManager.startOrResume();
    this.orchestrator.sessionManager = this.sessionManager;
    if (sessionResult.resumed) {
      this.orchestrator.todos = sessionResult.session.todos || [];
    }

    const initTime = Date.now() - initStart;

    // 16. Route to mode
    if (this.config.web) {
      await this.startWebMode(initTime);
    } else {
      this.cli.printBanner();
      this.cli.printProjectInfo(this.projectContext.getSummary());
      this.printInitStats(initTime, sessionResult.resumed);
      await this.startCLIMode();
    }
  }

  printInitStats(initTimeMs, sessionResumed) {
    const stats = this.symbolIndex.getStats();
    if (stats.totalSymbols > 0) {
      this.cli.print(chalk.dim(`  Symbol Index: ${stats.totalSymbols} symbols, ${stats.totalReferences} refs (${stats.buildTimeMs}ms)`));
    }
    if (this.blueprintContext.hasBlueprints()) {
      const bp = [];
      if (this.blueprintContext.prd) bp.push('PRD');
      if (this.blueprintContext.serviceMap) bp.push('ServiceMap');
      if (this.blueprintContext.uiMap) bp.push('UIMap');
      this.cli.print(chalk.dim(`  Blueprints: ${bp.join(' + ')} loaded`));
    }
    const providerName = this.orchestrator?.client?.name || this.config.llmProvider || 'unknown';
    this.cli.print(chalk.dim(`  LLM Provider: ${providerName}`));
    this.cli.print(chalk.dim(`  Workflow Engine: VL-Workflow-Engine vendor copy (.codex, JS, JSON v${WORKFLOW_VERSION}, spec doc v${WORKFLOW_SPEC_DOC_VERSION})`));
    if (sessionResumed) {
      this.cli.print(chalk.dim('  Session: Resumed'));
    }
    this.cli.print(chalk.dim(`  Init: ${initTimeMs}ms total\n`));
  }

  async startWebMode(initTime) {
    const { WebServer } = await import('./server/server.js');

    this.webServer = new WebServer({
      config: this.config,
      orchestrator: this.orchestrator,
      projectContext: this.projectContext,
      contextManager: this.contextManager,
      toolRegistry: this.toolRegistry,
      symbolIndex: this.symbolIndex,
      impactAnalyzer: this.impactAnalyzer,
      autoFix: this.autoFix,
      localWorkspace: this.localWorkspace,
      blueprintContext: this.blueprintContext,
    });

    // File watcher with SSE broadcast
    const fileWatcherCallbacks = {
      onChange: async (filePath, changeType) => {
        this.webServer.broadcast({ type: 'file_changed', path: filePath, changeType });
        this.fileCache.invalidate(filePath);

        if (changeType !== 'deleted' && filePath.match(/\.(vx|sc|cp|vs|vdb|vth)$/)) {
          await this.symbolIndex.updateFile(filePath);
        }

        if (changeType !== 'deleted' && filePath.match(/\.(vx|sc|cp|vs|vdb)$/)) {
          try {
            const result = await this.toolRegistry.execute('VLValidate', { file_path: filePath });
            this.webServer.broadcast({ type: 'validation_result', path: filePath, result: result.result || result.error });
          } catch { /* mid-edit */ }
        }

        if (changeType !== 'deleted' && filePath.match(/\.(vx|sc|cp|vs|vdb)$/) && this.projectContext.projectMeta) {
          try {
            const fs = await import('fs/promises');
            const content = await fs.readFile(filePath, 'utf-8');
            const relativePath = filePath.startsWith(this.config.workDir)
              ? filePath.substring(this.config.workDir.length + 1) : filePath;
            const extracted = extractSingleFile(relativePath, content, this.config.workDir);
            if (extracted) {
              mergeIntoMeta(this.projectContext.projectMeta, extracted);
              const validation = validateMeta(this.projectContext.projectMeta);
              this.webServer.broadcast({ type: 'meta_updated', path: filePath, valid: validation.valid, issues: validation.issues.slice(0, 10) });
              this.blueprintContext.loadFromProjectMeta(this.projectContext.projectMeta);
            }
          } catch { /* extraction failed */ }
        }

        if (changeType === 'created' || changeType === 'deleted') {
          await this.projectContext.load();
          await this.smartContext.buildGraph();
          await this.symbolIndex.build();
          this.webServer.broadcast({ type: 'file_tree_updated' });
        }

        this.sessionManager?.addFileChange(filePath, changeType);
      },
    };
    this.fileWatcher = new FileWatcher(this.config.workDir, fileWatcherCallbacks);
    this.fileWatcher.start();

    this.config._onWorkspaceSwitched = async (dirPath, { noWorkspace = false } = {}) => {
      this.fileWatcher?.stop();
      this.fileWatcher = new FileWatcher(dirPath, fileWatcherCallbacks);
      this.fileWatcher.start();
      this.fileCache = new FileCache(dirPath);
      this.config.fileCache = this.fileCache;
      this.hooksManager = new HooksManager(dirPath);
      this.sessionManager = new SessionManager(dirPath);
      await this.sessionManager.init();
      this.orchestrator.sessionManager = this.sessionManager;
    };

    // Warm-up API cache
    this.orchestrator.warmUp().catch(() => {});

    // Pick a free port — prefer the configured one, else find next available
    let resolvedPort = this.config.port;
    try {
      resolvedPort = await findFreePort(Math.max(this.config.port, 4000), 4099);
    } catch {}
    const port = await this.webServer.start(resolvedPort);

    // Register this instance so other windows can discover it
    registerInstance(port, this.config.noWorkspace ? '' : this.config.workDir, process.pid);

    console.log(chalk.bold.cyan(`
╔══════════════════════════════════════════╗
║     VLCode Lite v${APP_VERSION}${' '.repeat(Math.max(0, 22 - APP_VERSION.length))}║
║  Standalone AI IDE for VL v3.7          ║
╚══════════════════════════════════════════╝`));
    console.log(chalk.dim(`  Model: ${this.config.model}`));
    console.log(chalk.dim(`  Work Dir: ${this.config.noWorkspace ? '[no workspace selected]' : this.config.workDir}`));
    if (this.projectContext.isVLProject()) {
      const summary = this.projectContext.getSummary();
      const stats = this.symbolIndex.getStats();
      console.log(chalk.green(`  VL Project: ${summary.totalFiles} files (${summary.breakdown})`));
      console.log(chalk.dim(`  Symbols: ${stats.totalSymbols} indexed, ${stats.totalReferences} cross-refs`));
    }
    console.log(chalk.dim(`  Init: ${initTime}ms`));
    console.log('');
    console.log(chalk.bold.green(`  ✦ Browser IDE: http://localhost:${port}`));
    console.log(chalk.dim(`  Press Ctrl+C to stop\n`));

    const _shutdown = async () => {
      deregisterInstance(port);
      console.log(chalk.dim('\nSaving session...'));
      await this.sessionManager?.save();
      this.fileWatcher?.stop();
      process.exit(0);
    };
    process.on('SIGINT', _shutdown);
    process.on('SIGTERM', _shutdown);
  }

  async startCLIMode() {
    const readline = (await import('readline')).default;
    const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
    const prompt = () => {
      rl.question(chalk.bold.cyan('You: '), async (input) => {
        const trimmed = input.trim();
        if (!trimmed) return prompt();
        if (trimmed === '/quit' || trimmed === '/exit') {
          await this.sessionManager?.save();
          process.exit(0);
        }
        try {
          const response = await this.orchestrator.processUserMessage(trimmed);
          console.log(chalk.bold.green('AI: ') + response);
        } catch (e) {
          console.error(chalk.red(`Error: ${e.message}`));
        }
        prompt();
      });
    };
    prompt();
  }
}
