/**
 * SpecVerse v3.5 VS Code Extension
 * 
 * Provides language support for SpecVerse v3.5 .specly files (YAML + Conventions + AI Inference)
 */

import * as vscode from 'vscode';
import * as yaml from 'js-yaml';
import { readFileSync, existsSync } from 'fs';
import { join, dirname, basename, extname } from 'path';
import { exec } from 'child_process';
import { promisify } from 'util';

const execAsync = promisify(exec);

export function activate(context: vscode.ExtensionContext) {
  console.log('SpecVerse v3.5 extension activated');

  // Register providers
  const provider = new SpecVerseProvider(context);
  
  // Diagnostics collection
  const diagnostics = vscode.languages.createDiagnosticCollection('specverse');
  context.subscriptions.push(diagnostics);

  // Document validation on save
  const onSaveListener = vscode.workspace.onDidSaveTextDocument(async (document) => {
    if (isSpecVerseDocument(document)) {
      const config = vscode.workspace.getConfiguration('specverse');
      if (config.get('validation.onSave')) {
        await validateDocument(document, diagnostics);
      }
      
      if (config.get('diagrams.autoGenerate')) {
        await generateDiagramForDocument(document);
      }
    }
  });
  context.subscriptions.push(onSaveListener);

  // Document validation on change (debounced)
  let validationTimeout: NodeJS.Timeout;
  const onChangeListener = vscode.workspace.onDidChangeTextDocument((event) => {
    const document = event.document;
    if (isSpecVerseDocument(document)) {
      clearTimeout(validationTimeout);
      validationTimeout = setTimeout(async () => {
        const config = vscode.workspace.getConfiguration('specverse');
        if (config.get('validation.enabled')) {
          await validateDocument(document, diagnostics);
        }
      }, 1000);
    }
  });
  context.subscriptions.push(onChangeListener);

  // Register auto-generated CLI commands dynamically
  const packageJson = require('../package.json');
  const autoGeneratedCommands = packageJson.contributes.commands
    .filter((cmd: any) => cmd.command.startsWith('specverse.'))
    .map((cmd: any) => {
      const commandName = cmd.command.replace('specverse.', '');
      return vscode.commands.registerCommand(cmd.command, async (uri?: vscode.Uri) => {
        const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
        if (document && isSpecVerseDocument(document)) {
          await executeCliCommand(document, commandName);
        }
      });
    });

  // Register manual/custom commands
  const commands = [
    vscode.commands.registerCommand('specverse.validate', async (uri?: vscode.Uri) => {
      const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
      if (document && isSpecVerseDocument(document)) {
        await validateDocument(document, diagnostics);
        vscode.window.showInformationMessage('SpecVerse validation completed');
      }
    }),

    vscode.commands.registerCommand('specverse.format', async () => {
      const editor = vscode.window.activeTextEditor;
      if (editor && isSpecVerseDocument(editor.document)) {
        await formatDocument(editor);
      }
    }),

    vscode.commands.registerCommand('specverse.generateDiagram', async (uri?: vscode.Uri) => {
      const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
      if (document && isSpecVerseDocument(document)) {
        await generateDiagramForDocument(document);
      }
    }),

    vscode.commands.registerCommand('specverse.generateDocs', async (uri?: vscode.Uri) => {
      const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
      if (document && isSpecVerseDocument(document)) {
        await generateDocsForDocument(document);
      }
    }),

    vscode.commands.registerCommand('specverse.generateAI', async (uri?: vscode.Uri) => {
      const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
      if (document && isSpecVerseDocument(document)) {
        await generateAIForDocument(document);
      }
    }),

    vscode.commands.registerCommand('specverse.processSpecly', async (uri?: vscode.Uri) => {
      const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
      if (document && document.fileName.endsWith('.specly')) {
        await processSpeclyDocument(document);
      } else {
        vscode.window.showErrorMessage('This command only works with .specly files');
      }
    }),

    vscode.commands.registerCommand('specverse.infer', async (uri?: vscode.Uri) => {
      const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
      if (document && isSpecVerseDocument(document)) {
        await inferCompleteSystemForDocument(document);
      }
    }),

    vscode.commands.registerCommand('specverse.testCycle', async (uri?: vscode.Uri) => {
      const document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
      if (document && isSpecVerseDocument(document)) {
        await runTestCycleForDocument(document);
      }
    }),

    vscode.commands.registerCommand('specverse.batchGenerate', async () => {
      await runBatchDiagramGeneration();
    }),

    vscode.commands.registerCommand('specverse.migrate', async (uri?: vscode.Uri) => {
      // Determine the directory to migrate
      let targetDir: string;

      if (uri) {
        // URI provided - could be file or folder
        const stat = await vscode.workspace.fs.stat(uri);
        if (stat.type === vscode.FileType.Directory) {
          targetDir = uri.fsPath;
        } else {
          // It's a file - use its parent directory
          targetDir = dirname(uri.fsPath);
        }
      } else if (vscode.window.activeTextEditor) {
        // No URI - use active editor's directory
        targetDir = dirname(vscode.window.activeTextEditor.document.fileName);
      } else if (vscode.workspace.workspaceFolders?.[0]) {
        // Fallback to workspace root
        targetDir = vscode.workspace.workspaceFolders[0].uri.fsPath;
      } else {
        vscode.window.showErrorMessage('No directory to migrate. Open a file or folder first.');
        return;
      }

      await runMigrateCommand(targetDir);
    })
  ];

  context.subscriptions.push(...commands, ...autoGeneratedCommands);

  // Register completion provider for .specly files
  const completionProvider = vscode.languages.registerCompletionItemProvider(
    { scheme: 'file', language: 'specly' },
    new SpecVerseCompletionProvider(),
    ':',
    ' '
  );
  context.subscriptions.push(completionProvider);

  // Register hover provider for .specly files
  const hoverProvider = vscode.languages.registerHoverProvider(
    { scheme: 'file', language: 'specly' },
    new SpecVerseHoverProvider()
  );
  context.subscriptions.push(hoverProvider);

  // Status bar
  const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
  statusBar.command = 'specverse.validate';
  context.subscriptions.push(statusBar);

  // Update status bar on active editor change
  const onActiveEditorChange = vscode.window.onDidChangeActiveTextEditor((editor) => {
    if (editor && isSpecVerseDocument(editor.document)) {
      statusBar.text = '$(check) SpecVerse';
      statusBar.tooltip = 'Click to validate SpecVerse file';
      statusBar.show();
    } else {
      statusBar.hide();
    }
  });
  context.subscriptions.push(onActiveEditorChange);

  // Initial status bar update
  if (vscode.window.activeTextEditor && isSpecVerseDocument(vscode.window.activeTextEditor.document)) {
    statusBar.text = '$(check) SpecVerse';
    statusBar.tooltip = 'Click to validate SpecVerse file';
    statusBar.show();
  }
}

class SpecVerseProvider {
  constructor(private context: vscode.ExtensionContext) {}

  getCliPath(): string {
    const config = vscode.workspace.getConfiguration('specverse');
    return config.get('cli.path') || 'specverse';
  }
}

class SpecVerseCompletionProvider implements vscode.CompletionItemProvider {
  provideCompletionItems(
    document: vscode.TextDocument,
    position: vscode.Position,
    token: vscode.CancellationToken,
    context: vscode.CompletionContext
  ): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList<vscode.CompletionItem>> {
    
    if (!isSpecVerseDocument(document)) {
      return [];
    }

    const line = document.lineAt(position).text;
    const linePrefix = line.substring(0, position.character);
    
    // Detect context
    const context_info = analyzeContext(document, position);
    
    const completions: vscode.CompletionItem[] = [];

    // Type completions for attributes
    if (context_info.isAttributeType) {
      const types = [
        'String', 'Text', 'Integer', 'Number', 'Boolean', 
        'Date', 'DateTime', 'UUID', 'Email', 'URL', 
        'JSON', 'Object', 'Array'
      ];
      
      types.forEach(type => {
        const item = new vscode.CompletionItem(type, vscode.CompletionItemKind.TypeParameter);
        item.documentation = new vscode.MarkdownString(`SpecVerse type: \`${type}\``);
        completions.push(item);
      });
    }

    // Modifier completions
    if (context_info.isModifier) {
      const modifiers = [
        { name: 'required', description: 'Field is required' },
        { name: 'unique', description: 'Field must be unique' },
        { name: 'searchable', description: 'Field is searchable' },
        { name: 'verified', description: 'Field requires verification' },
        { name: 'auto=uuid4', description: 'Auto-generate UUID' },
        { name: 'auto=now', description: 'Auto-set current timestamp' },
        { name: 'auto=token', description: 'Auto-generate token' },
        { name: 'default=', description: 'Default value' },
        { name: 'min=', description: 'Minimum value' },
        { name: 'max=', description: 'Maximum value' }
      ];
      
      modifiers.forEach(mod => {
        const item = new vscode.CompletionItem(mod.name, vscode.CompletionItemKind.Keyword);
        item.documentation = new vscode.MarkdownString(mod.description);
        if (mod.name.endsWith('=')) {
          item.insertText = mod.name;
          item.command = { command: 'editor.action.triggerSuggest', title: 'Trigger Suggest' };
        }
        completions.push(item);
      });
    }

    // Relationship type completions
    if (context_info.isRelationshipType) {
      const relationships = [
        { name: 'hasMany', description: 'One-to-many relationship' },
        { name: 'hasOne', description: 'One-to-one relationship' },
        { name: 'belongsTo', description: 'Many-to-one relationship' },
        { name: 'manyToMany', description: 'Many-to-many relationship' }
      ];
      
      relationships.forEach(rel => {
        const item = new vscode.CompletionItem(rel.name, vscode.CompletionItemKind.Keyword);
        item.documentation = new vscode.MarkdownString(rel.description);
        completions.push(item);
      });
    }

    // Section completions
    if (context_info.isTopLevel) {
      const sections = [
        // v3.5 container format
        'components', 'deployments',
        // Component-level sections
        'component', 'deployment', 'version', 'description', 'tags',
        'import', 'export', 'models', 'controllers', 'services', 
        'views', 'events',
        // v3.5 CURED operations
        'cured',
        // Deployment sections
        'instances', 'environment', 'communications',
        // v3.5 advanced features
        'profiles', 'profile-attachment', 'steps', 'subscribes_to'
      ];
      
      sections.forEach(section => {
        const item = new vscode.CompletionItem(section, vscode.CompletionItemKind.Module);
        if (section === 'components') {
          item.documentation = new vscode.MarkdownString(`SpecVerse v3.5 container format: array of components`);
        } else if (section === 'deployments') {
          item.documentation = new vscode.MarkdownString(`SpecVerse v3.5 container format: array of deployments`);
        } else if (section === 'cured') {
          item.documentation = new vscode.MarkdownString(`SpecVerse v3.5 CURED operations: Create, Update, Retrieve, Evolve, Destroy`);
        } else if (section === 'instances') {
          item.documentation = new vscode.MarkdownString(`SpecVerse v3.5 deployment instances: controllers, services, views, communications`);
        } else if (section === 'communications') {
          item.documentation = new vscode.MarkdownString(`SpecVerse v3.5 communication channels: pubsub, rpc, queue, streaming`);
        } else {
          item.documentation = new vscode.MarkdownString(`SpecVerse section: \`${section}\``);
        }
        completions.push(item);
      });
    }

    // Deployment property completions
    if (context_info.isDeploymentProperty) {
      const deploymentProps = [
        { name: 'component', description: 'Component reference for this instance' },
        { name: 'namespace', description: 'Logical namespace for this instance' },
        { name: 'advertises', description: 'Capabilities this instance advertises' },
        { name: 'uses', description: 'Capabilities this instance uses' },
        { name: 'scale', description: 'Number of instances to run' },
        { name: 'capabilities', description: 'Available capabilities on communication channel' },
        { name: 'type', description: 'Communication channel type (pubsub, rpc, queue, streaming)' }
      ];
      
      deploymentProps.forEach(prop => {
        const item = new vscode.CompletionItem(prop.name, vscode.CompletionItemKind.Property);
        item.documentation = new vscode.MarkdownString(prop.description);
        completions.push(item);
      });
    }

    return completions;
  }
}

class SpecVerseHoverProvider implements vscode.HoverProvider {
  provideHover(
    document: vscode.TextDocument,
    position: vscode.Position,
    token: vscode.CancellationToken
  ): vscode.ProviderResult<vscode.Hover> {
    
    if (!isSpecVerseDocument(document)) {
      return;
    }

    const line = document.lineAt(position).text;
    const wordRange = document.getWordRangeAtPosition(position);
    const word = wordRange ? document.getText(wordRange) : '';

    // Detect attribute definitions
    const attributeMatch = line.match(/^\s*(\w+):\s*(\w+)(.*)/);
    if (attributeMatch && wordRange) {
      const [, name, type, modifiers] = attributeMatch;
      
      if (word === type) {
        return new vscode.Hover([
          `**SpecVerse Type:** \`${type}\``,
          '',
          getTypeDocumentation(type)
        ]);
      }
      
      if (word === name) {
        return new vscode.Hover([
          `**Attribute:** \`${name}\``,
          `**Type:** \`${type}\``,
          `**Modifiers:** \`${modifiers.trim() || 'none'}\``,
          '',
          'SpecVerse attribute definition using conventions'
        ]);
      }
    }

    // Detect lifecycle flows
    const flowMatch = line.match(/flow:\s*(.+)/);
    if (flowMatch && line.includes(word)) {
      return new vscode.Hover([
        '**SpecVerse Lifecycle Flow**',
        '',
        'Shorthand lifecycle definition.',
        'States are connected with `->` transitions.',
        '',
        'Example: `draft -> published -> archived`'
      ]);
    }

    // Detect executable properties keywords
    if (word === 'steps') {
      return new vscode.Hover([
        '**Steps** *(Array<string>)*',
        '',
        'Sequential steps describing the execution process.',
        '',
        '**Usage:** Available in behaviors, controller actions, service operations, and CURED operations.',
        '',
        '**Example:**',
        '```yaml',
        'steps:',
        '  - "Validate input data"',
        '  - "Process business logic"',
        '  - "Return results"',
        '```'
      ]);
    }

    if (word === 'parameters') {
      return new vscode.Hover([
        '**Parameters** *(Object)*',
        '',
        'Input parameters for executable properties (actions, operations, behaviors).',
        '',
        '**Usage:** Replaces `payload:` in v3.5 - defines input data structure.',
        '',
        '**Example:**',
        '```yaml',
        'parameters:',
        '  userId: UUID required',
        '  name: String required max=100',
        '```'
      ]);
    }

    if (word === 'publishes') {
      return new vscode.Hover([
        '**Publishes** *(Array<string>)*',
        '',
        'Events published by this executable property.',
        '',
        '**Usage:** Replaces `events:` in v3.5 - defines output events.',
        '',
        '**Example:**',
        '```yaml',
        'publishes: [UserCreated, EmailSent]',
        '```'
      ]);
    }

    if (word === 'subscribes_to') {
      return new vscode.Hover([
        '**Subscribes To** *(Array<string> | Object)*',
        '',
        'Events this component subscribes to.',
        '',
        '**Formats:**',
        '- Array: `["EventName1", "EventName2"]`',
        '- Object: `EventName: "handlerMethod"`',
        '',
        '**Example:**',
        '```yaml',
        'subscribes_to:',
        '  - UserCreated',
        '  - OrderPlaced',
        '```'
      ]);
    }

    if (word === 'profiles') {
      return new vscode.Hover([
        '**Profiles** *(Array<string>)*',
        '',
        'Runtime profiles that can be applied to models.',
        '',
        '**Usage:** Defines which profiles are available for profile attachment.',
        '',
        '**Example:**',
        '```yaml',
        'profiles: ["CustomerProfile", "AdminProfile"]',
        '```'
      ]);
    }

    if (word === 'profile-attachment') {
      return new vscode.Hover([
        '**Profile Attachment** *(Object)*',
        '',
        'Configuration for attaching profiles to models at runtime.',
        '',
        '**Properties:**',
        '- `profiles`: Array of available profiles',
        '- `conditions`: Optional conditions for attachment',
        '- `priority`: Attachment priority (1-10)',
        '',
        '**Example:**',
        '```yaml',
        'profile-attachment:',
        '  profiles: ["VisibilityProfile"]',
        '  conditions: ["user.role === admin"]',
        '  priority: 5',
        '```'
      ]);
    }

    // Detect deployment keywords
    if (word === 'instances') {
      return new vscode.Hover([
        '**Instances** *(Object)*',
        '',
        'Logical deployment instances for controllers, services, views, and communications.',
        '',
        '**Structure:**',
        '- `controllers`: Controller instance configurations',
        '- `services`: Service instance configurations', 
        '- `views`: View instance configurations',
        '- `communications`: Communication channel definitions'
      ]);
    }

    if (word === 'advertises') {
      return new vscode.Hover([
        '**Advertises** *(string | Array<string>)*',
        '',
        'Capabilities that this instance makes available to other instances.',
        '',
        '**Common patterns:**',
        '- `"*"` - All capabilities',
        '- `"behaviors.*"` - All behavior capabilities',
        '- `["create", "read", "update"]` - Specific operations'
      ]);
    }

    if (word === 'uses') {
      return new vscode.Hover([
        '**Uses** *(Array<string>)*',
        '',
        'Capabilities that this instance consumes from communication channels.',
        '',
        '**Examples:**',
        '- `["user.*", "order.create"]` - Specific capability patterns',
        '- `["database.*"]` - All database capabilities'
      ]);
    }

    if (word === 'capabilities') {
      return new vscode.Hover([
        '**Capabilities** *(Array<string>)*',
        '',
        'List of capabilities available on this communication channel.',
        '',
        '**Used by:** Communication channel definitions to specify what operations are available for instances to advertise/use.'
      ]);
    }

    return;
  }
}

// Helper functions

function isSpecVerseDocument(document: vscode.TextDocument): boolean {
  return (
    document.languageId === 'specly' ||
    document.fileName.endsWith('.specly')
  );
}

function analyzeContext(document: vscode.TextDocument, position: vscode.Position) {
  const line = document.lineAt(position).text;
  const linePrefix = line.substring(0, position.character);
  
  // Check if we're in attributes section
  const isInAttributesSection = (() => {
    for (let i = position.line; i >= 0; i--) {
      const checkLine = document.lineAt(i).text;
      if (checkLine.trim() === 'attributes:') return true;
      if (checkLine.trim().endsWith(':') && !checkLine.includes('  ')) return false;
    }
    return false;
  })();

  // Check if we're in relationships section
  const isInRelationshipsSection = (() => {
    for (let i = position.line; i >= 0; i--) {
      const checkLine = document.lineAt(i).text;
      if (checkLine.trim() === 'relationships:') return true;
      if (checkLine.trim().endsWith(':') && !checkLine.includes('  ')) return false;
    }
    return false;
  })();

  // Check if we're in deployment instances section
  const isInDeploymentSection = (() => {
    for (let i = position.line; i >= 0; i--) {
      const checkLine = document.lineAt(i).text;
      if (checkLine.trim() === 'instances:' || checkLine.trim() === 'communications:') return true;
      if (checkLine.trim().endsWith(':') && !checkLine.includes('    ')) return false;
    }
    return false;
  })();

  return {
    isAttributeType: isInAttributesSection && linePrefix.match(/^\s*\w+:\s*$/),
    isModifier: isInAttributesSection && linePrefix.match(/^\s*\w+:\s*\w+\s+$/),
    isRelationshipType: isInRelationshipsSection && linePrefix.match(/^\s*\w+:\s*$/),
    isTopLevel: !linePrefix.includes('  ') && linePrefix.trim().endsWith(':'),
    isDeploymentProperty: isInDeploymentSection && linePrefix.match(/^\s+\w+:\s*$/)
  };
}

function getTypeDocumentation(type: string): string {
  const docs: { [key: string]: string } = {
    'String': 'Variable-length text field (default max 255 characters)',
    'Text': 'Long text field for large content',
    'Integer': 'Whole number',
    'Number': 'Decimal number',
    'Boolean': 'True/false value',
    'Date': 'Date without time',
    'DateTime': 'Date and time timestamp',
    'UUID': 'Universally Unique Identifier',
    'Email': 'Email address with validation',
    'URL': 'Web URL with validation',
    'JSON': 'JSON object or array',
    'Object': 'Structured object',
    'Array': 'List of values'
  };
  
  return docs[type] || 'SpecVerse data type';
}

async function validateDocument(document: vscode.TextDocument, diagnostics: vscode.DiagnosticCollection) {
  try {
    diagnostics.clear();
    
    const config = vscode.workspace.getConfiguration('specverse');
    const cliPath = config.get('cli.path') || 'specverse';
    
    const workingDir = dirname(document.fileName);
    const { stdout, stderr } = await execAsync(`"${cliPath}" validate "${document.fileName}"`, { cwd: workingDir });
    
    // Success - clear any existing diagnostics
    if (stdout.includes('✅') && !stderr) {
      return;
    }
    
    // Parse validation errors from both stdout and stderr
    const errors: vscode.Diagnostic[] = [];
    const allOutput = (stdout + '\n' + stderr).split('\n');

    allOutput.forEach((line) => {
      line = line.trim();
      if (!line) return;

      // Parse different error patterns
      let errorMessage = '';
      let severity = vscode.DiagnosticSeverity.Error;
      let lineNumber = 0;

      // Pattern 1: Numbered validation errors from CLI (e.g., "   1. /components/Test: Unknown property")
      const numberedErrorMatch = line.match(/^\d+\.\s*(.+)$/);
      if (numberedErrorMatch) {
        errorMessage = numberedErrorMatch[1].trim();
      }
      // Pattern 2: ❌ Error messages
      else if (line.includes('❌') && !line.includes('Validation failed')) {
        errorMessage = line.replace(/❌/g, '').trim();
      }
      // Pattern 3: Error: prefix
      else if (line.includes('Error:')) {
        errorMessage = line.replace(/Error:/, '').trim();
      }
      // Pattern 4: Warning messages
      else if (line.includes('⚠️') || line.includes('Warning:')) {
        errorMessage = line.replace(/⚠️|Warning:/, '').trim();
        severity = vscode.DiagnosticSeverity.Warning;
      }
      // Pattern 5: Schema validation errors (often contain "at line X")
      else if (line.includes('at line') || line.includes('line:')) {
        const lineMatch = line.match(/(?:at line|line:?\s*)(\d+)/i);
        if (lineMatch) {
          lineNumber = Math.max(0, parseInt(lineMatch[1]) - 1); // Convert to 0-based
        }
        errorMessage = line;
      }
      // Pattern 6: JSON Schema errors
      else if (line.includes('should') && (line.includes('property') || line.includes('type') || line.includes('required'))) {
        errorMessage = line;
        severity = vscode.DiagnosticSeverity.Error;
      }
      // Pattern 7: Parser errors
      else if (line.includes('Parse error') || line.includes('Syntax error')) {
        errorMessage = line;
      }
      // Skip validation header lines (don't create diagnostics for these)
      else if (line.includes('Validation failed') || line.includes('Validation Errors:') || line.includes('📝')) {
        return; // Skip these header lines
      }

      if (errorMessage) {
        const diagnostic = new vscode.Diagnostic(
          new vscode.Range(lineNumber, 0, lineNumber, Number.MAX_SAFE_INTEGER),
          errorMessage,
          severity
        );
        diagnostic.source = 'SpecVerse';
        errors.push(diagnostic);
      }
    });
    
    // If we found errors, set them
    if (errors.length > 0) {
      diagnostics.set(document.uri, errors);
    } else if (!stdout.includes('✅')) {
      // CLI ran but no clear success message and no parseable errors
      const diagnostic = new vscode.Diagnostic(
        new vscode.Range(0, 0, 0, 0),
        'SpecVerse validation completed with unknown status. Check output for details.',
        vscode.DiagnosticSeverity.Information
      );
      diagnostic.source = 'SpecVerse';
      diagnostics.set(document.uri, [diagnostic]);
    }
    
  } catch (error: any) {
    // CLI execution error - parse stdout for validation errors
    if (error.code === 'ENOENT') {
      const diagnostic = new vscode.Diagnostic(
        new vscode.Range(0, 0, 0, 0),
        'SpecVerse CLI not found. Please configure cli.path in settings or ensure specverse is on PATH',
        vscode.DiagnosticSeverity.Error
      );
      diagnostic.source = 'SpecVerse';
      diagnostics.set(document.uri, [diagnostic]);
      return;
    }
    
    // Parse validation errors from stdout when command fails (CLI outputs errors to stdout)
    if (error.stdout) {
      const errors: vscode.Diagnostic[] = [];
      const errorLines = error.stdout.split('\n');

      errorLines.forEach((line) => {
        line = line.trim();
        if (!line) return;

        let errorMessage = '';
        let severity = vscode.DiagnosticSeverity.Error;
        let lineNumber = 0;

        // Pattern 1: Numbered validation errors from CLI (e.g., "   1. /components/Test: Unknown property")
        const numberedErrorMatch = line.match(/^\d+\.\s*(.+)$/);
        if (numberedErrorMatch) {
          errorMessage = numberedErrorMatch[1].trim();
        }
        // Pattern 2: ❌ Error messages from CLI
        else if (line.includes('❌') && !line.includes('Validation failed')) {
          errorMessage = line.replace(/❌/g, '').trim();
        }
        // Pattern 3: Direct error descriptions (like "Unknown property 'webservice'")
        else if (line.includes('Unknown property') || line.includes('should') || line.includes('required')) {
          errorMessage = line;
        }
        // Pattern 4: Path-based errors (like "/deployments/TestDeployment/instances:")
        else if (line.includes('/') && line.includes(':')) {
          errorMessage = line;
        }
        // Pattern 5: Warning messages
        else if (line.includes('⚠️') || line.includes('Warning:')) {
          errorMessage = line.replace(/⚠️|Warning:/, '').trim();
          severity = vscode.DiagnosticSeverity.Warning;
        }
        // Skip validation header lines (don't create diagnostics for these)
        else if (line.includes('Validation failed') || line.includes('Validation Errors:') || line.includes('📝')) {
          return; // Skip these header lines
        }

        if (errorMessage) {
          const diagnostic = new vscode.Diagnostic(
            new vscode.Range(lineNumber, 0, lineNumber, Number.MAX_SAFE_INTEGER),
            errorMessage,
            severity
          );
          diagnostic.source = 'SpecVerse';
          errors.push(diagnostic);
        }
      });

      if (errors.length > 0) {
        diagnostics.set(document.uri, errors);
        return;
      }
    }
    
    // Fallback for unknown errors
    const diagnostic = new vscode.Diagnostic(
      new vscode.Range(0, 0, 0, 0),
      `SpecVerse CLI error: ${error.message}`,
      vscode.DiagnosticSeverity.Error
    );
    diagnostic.source = 'SpecVerse';
    diagnostics.set(document.uri, [diagnostic]);
  }
}

async function formatDocument(editor: vscode.TextEditor) {
  try {
    const document = editor.document;
    const config = vscode.workspace.getConfiguration('specverse');
    const cliPath = config.get('cli.path') || 'specverse';
    
    // Format command is not available in current CLI, show message
    vscode.window.showInformationMessage('Format command is not currently available in SpecVerse CLI');
    return;
    
    if (stdout) {
      const edit = new vscode.WorkspaceEdit();
      const fullRange = new vscode.Range(
        document.positionAt(0),
        document.positionAt(document.getText().length)
      );
      edit.replace(document.uri, fullRange, stdout);
      await vscode.workspace.applyEdit(edit);
    }
    
  } catch (error: any) {
    vscode.window.showErrorMessage(`Format failed: ${error.message}`);
  }
}

async function generateDiagramForDocument(document: vscode.TextDocument) {
  try {
    const config = vscode.workspace.getConfiguration('specverse');
    const cliPath = config.get('cli.path') || 'specverse';
    
    const diagramType = await vscode.window.showQuickPick([
      'er', 'sequence', 'architecture', 'lifecycle', 'deployment', 'all'
    ], {
      placeHolder: 'Select diagram type to generate'
    });
    
    if (!diagramType) return;
    
    const workingDir = dirname(document.fileName);
    const baseName = basename(document.fileName, extname(document.fileName));
    const outputFile = join(workingDir, `${baseName}-${diagramType}.mmd`);
    
    console.log(`[SpecVerse] Working directory: ${workingDir}`);
    console.log(`[SpecVerse] Output file: ${outputFile}`);
    console.log(`[SpecVerse] Command: "${cliPath}" gen uml "${document.fileName}" --output "${outputFile}"`);
    
    await execAsync(`"${cliPath}" gen uml "${document.fileName}" --output "${outputFile}"`, { cwd: workingDir });
    vscode.window.showInformationMessage(`${diagramType} diagram generated successfully`);
    
  } catch (error: any) {
    vscode.window.showErrorMessage(`Diagram generation failed: ${error.message}`);
  }
}

async function generateDocsForDocument(document: vscode.TextDocument) {
  try {
    const config = vscode.workspace.getConfiguration('specverse');
    const cliPath = config.get('cli.path') || 'specverse';
    
    const format = await vscode.window.showQuickPick([
      'markdown', 'html', 'openapi'
    ], {
      placeHolder: 'Select documentation format'
    });
    
    if (!format) return;
    
    const workingDir = dirname(document.fileName);
    await execAsync(`"${cliPath}" gen docs "${document.fileName}"`, { cwd: workingDir });
    vscode.window.showInformationMessage(`${format} documentation generated successfully`);
    
  } catch (error: any) {
    vscode.window.showErrorMessage(`Documentation generation failed: ${error.message}`);
  }
}

async function generateAIForDocument(document: vscode.TextDocument) {
  try {
    const config = vscode.workspace.getConfiguration('specverse');
    const cliPath = config.get('cli.path') || 'specverse';
    
    const workingDir = dirname(document.fileName);
    await execAsync(`"${cliPath}" gen views "${document.fileName}"`, { cwd: workingDir });
    vscode.window.showInformationMessage('AI-optimized specification generated successfully');
    
  } catch (error: any) {
    vscode.window.showErrorMessage(`AI generation failed: ${error.message}`);
  }
}

async function processSpeclyDocument(document: vscode.TextDocument) {
  try {
    const config = vscode.workspace.getConfiguration('specverse');
    const cliPath = config.get('cli.path') || 'specverse';
    
    const workingDir = dirname(document.fileName);
    const outputFile = document.fileName.replace('.specly', '.yaml');
    await execAsync(`"${cliPath}" gen yaml "${document.fileName}" --output "${outputFile}"`, { cwd: workingDir });
    
    vscode.window.showInformationMessage(`Generated expanded YAML: ${outputFile}`);
    
    // Ask if user wants to open the generated file
    const openFile = await vscode.window.showInformationMessage(
      'Open generated .yaml file?',
      'Open',
      'Cancel'
    );
    
    if (openFile === 'Open') {
      const yamlDocument = await vscode.workspace.openTextDocument(outputFile);
      await vscode.window.showTextDocument(yamlDocument);
    }
    
  } catch (error: any) {
    vscode.window.showErrorMessage(`Processing failed: ${error.message}`);
  }
}

async function inferCompleteSystemForDocument(document: vscode.TextDocument) {
  try {
    const config = vscode.workspace.getConfiguration('specverse');
    const cliPath = config.get('cli.path') || 'specverse';
    
    if (!config.get('inference.enabled')) {
      vscode.window.showInformationMessage('AI inference is disabled in settings');
      return;
    }
    
    const outputFile = document.fileName.replace('.specly', '-inferred.specly');
    await vscode.window.withProgress({
      location: vscode.ProgressLocation.Notification,
      title: 'Running AI inference...',
      cancellable: false
    }, async (progress) => {
      progress.report({ message: 'Generating complete system architecture' });
      const workingDir = dirname(document.fileName);
      await execAsync(`node dist/inference-engine/test-cli.js "${document.fileName}" -o "${outputFile}"`, { cwd: workingDir });
    });
    
    vscode.window.showInformationMessage(`AI-inferred complete system generated: ${outputFile}`);
    
    // Ask if user wants to open the generated file
    const openFile = await vscode.window.showInformationMessage(
      'Open AI-inferred complete system?',
      'Open',
      'Cancel'
    );
    
    if (openFile === 'Open') {
      const inferredDocument = await vscode.workspace.openTextDocument(outputFile);
      await vscode.window.showTextDocument(inferredDocument);
    }
    
  } catch (error: any) {
    vscode.window.showErrorMessage(`AI inference failed: ${error.message}`);
  }
}

async function runTestCycleForDocument(document: vscode.TextDocument) {
  try {
    const config = vscode.workspace.getConfiguration('specverse');
    const cliPath = config.get('cli.path') || 'specverse';
    
    if (!config.get('testing.enableCycle')) {
      vscode.window.showInformationMessage('Test cycle is disabled in settings');
      return;
    }
    
    await vscode.window.withProgress({
      location: vscode.ProgressLocation.Notification,
      title: 'Running test cycle...',
      cancellable: false
    }, async (progress) => {
      progress.report({ message: 'Validating → Processing → Validating' });
      const workingDir = dirname(document.fileName);
      await execAsync(`"${cliPath}" test cycle "${document.fileName}"`, { cwd: workingDir });
    });
    
    vscode.window.showInformationMessage('Test cycle completed successfully');
    
  } catch (error: any) {
    vscode.window.showErrorMessage(`Test cycle failed: ${error.message}`);
  }
}

async function executeCliCommand(document: vscode.TextDocument, commandName: string) {
  // Map VSCode command names to exact CLI commands
  const commandMap: { [key: string]: string } = {
    // Single commands (unchanged)
    'validate': 'validate',
    'infer': 'infer',
    'init': 'init',
    'cache': 'cache',
    'help': 'help',
    'gen': 'gen',
    'dev': 'dev',
    'test': 'test',
    // realize requires a positional type arg; default the menu action to
    // "realize all" (full regeneration). Per-entity dispatch is a future
    // design — see TODO: "Derive realize subtypes from entity registry".
    'realize': 'realize all',
    
    // Grouped commands - map compressed names to CLI format
    'genyaml': 'gen yaml',
    'gendiagram': 'gen diagram',
    'gendocs': 'gen docs',
    'genviews': 'gen views',
    'genall': 'gen all',
    'devquick': 'dev quick',
    'devformat': 'dev format',
    'devwatch': 'dev watch',
    'testcycle': 'test cycle',
    'testbatch': 'test batch'
  };
  
  // Fall through: convert dotted form (e.g. "realize.all") to space-separated
  // CLI args (e.g. "realize all") so auto-generated subcommand entries work
  // without needing a commandMap entry for every one.
  const cliCommand = commandMap[commandName] || commandName.replace(/\./g, ' ');
  
  try {
    const config = vscode.workspace.getConfiguration('specverse');
    const cliPath = config.get('cli.path') || 'specverse';
    const workingDir = dirname(document.fileName);
    const fullCommand = `"${cliPath}" ${cliCommand} "${document.fileName}"`;
    
    console.log(`[SpecVerse] Executing: ${fullCommand}`);
    
    await vscode.window.withProgress({
      location: vscode.ProgressLocation.Notification,
      title: `Running SpecVerse ${cliCommand}...`,
      cancellable: false
    }, async (progress) => {
      progress.report({ message: 'Processing specification file' });
      const { stdout, stderr } = await execAsync(fullCommand, { cwd: workingDir });
      
      // Log all output for debugging
      if (stdout) console.log(`[SpecVerse] stdout:`, stdout);
      if (stderr) console.log(`[SpecVerse] stderr:`, stderr);
    });
    
    vscode.window.showInformationMessage(`SpecVerse ${cliCommand} completed successfully`);
    
  } catch (error: any) {
    console.log(`[SpecVerse] Command failed:`, error);
    
    // Create diagnostics collection for this command if it doesn't exist
    const diagnostics = vscode.languages.createDiagnosticCollection(`specverse-${commandName}`);
    
    // Parse CLI output errors (same logic as validateDocument)
    const errors: vscode.Diagnostic[] = [];
    
    // Check both stdout and stderr for errors
    const allOutput = ((error.stdout || '') + '\n' + (error.stderr || '')).split('\n');
    
    allOutput.forEach((line) => {
      line = line.trim();
      if (!line) return;

      let errorMessage = '';
      let severity = vscode.DiagnosticSeverity.Error;
      let lineNumber = 0;

      // Pattern 1: Numbered validation errors from CLI (e.g., "   1. /components/Test: Unknown property")
      const numberedErrorMatch = line.match(/^\d+\.\s*(.+)$/);
      if (numberedErrorMatch) {
        errorMessage = numberedErrorMatch[1].trim();
      }
      // Pattern 2: ❌ Error messages from CLI
      else if (line.includes('❌') && !line.includes('Validation failed')) {
        errorMessage = line.replace(/❌/g, '').trim();
      }
      // Pattern 3: Direct error descriptions
      else if (line.includes('Unknown property') || line.includes('should') || line.includes('required')) {
        errorMessage = line;
      }
      // Pattern 4: Path-based errors
      else if (line.includes('/') && line.includes(':')) {
        errorMessage = line;
      }
      // Pattern 5: Warning messages
      else if (line.includes('⚠️') || line.includes('Warning:')) {
        errorMessage = line.replace(/⚠️|Warning:/, '').trim();
        severity = vscode.DiagnosticSeverity.Warning;
      }
      // Skip validation header lines (don't create diagnostics for these)
      else if (line.includes('Validation failed') || line.includes('Validation Errors:') || line.includes('📝')) {
        return; // Skip these header lines
      }

      if (errorMessage) {
        const diagnostic = new vscode.Diagnostic(
          new vscode.Range(lineNumber, 0, lineNumber, Number.MAX_SAFE_INTEGER),
          errorMessage,
          severity
        );
        diagnostic.source = 'SpecVerse';
        errors.push(diagnostic);
      }
    });
    
    // Display errors in Problems panel
    if (errors.length > 0) {
      diagnostics.set(document.uri, errors);
      vscode.window.showErrorMessage(`SpecVerse ${cliCommand} failed with ${errors.length} errors. See Problems panel for details.`);
    } else {
      // Fallback error message
      const diagnostic = new vscode.Diagnostic(
        new vscode.Range(0, 0, 0, 0),
        `SpecVerse ${cliCommand} failed: ${error.message}`,
        vscode.DiagnosticSeverity.Error
      );
      diagnostic.source = 'SpecVerse';
      diagnostics.set(document.uri, [diagnostic]);
      vscode.window.showErrorMessage(`SpecVerse ${cliCommand} failed: ${error.message}`);
    }
  }
}

async function runMigrateCommand(targetDir: string) {
  try {
    const config = vscode.workspace.getConfiguration('specverse');
    const cliPath = config.get('cli.path') || 'specverse';

    // Ask user for confirmation and options
    const dryRun = await vscode.window.showQuickPick([
      { label: 'Migrate', description: 'Apply migration to files', value: false },
      { label: 'Dry Run', description: 'Preview changes without modifying files', value: true }
    ], {
      placeHolder: 'Choose migration mode'
    });

    if (!dryRun) return;

    const dryRunFlag = dryRun.value ? '--dry-run' : '';
    const fullCommand = `"${cliPath}" migrate ${dryRunFlag} "${targetDir}"`;

    console.log(`[SpecVerse] Executing: ${fullCommand}`);

    await vscode.window.withProgress({
      location: vscode.ProgressLocation.Notification,
      title: `Migrating specs to v3.5 format...`,
      cancellable: false
    }, async (progress) => {
      progress.report({ message: dryRun.value ? 'Previewing changes...' : 'Applying migration...' });
      const { stdout, stderr } = await execAsync(fullCommand, { cwd: targetDir });

      // Show output in output channel
      const outputChannel = vscode.window.createOutputChannel('SpecVerse Migration');
      outputChannel.clear();
      if (stdout) outputChannel.appendLine(stdout);
      if (stderr) outputChannel.appendLine(stderr);
      outputChannel.show();
    });

    vscode.window.showInformationMessage(
      dryRun.value
        ? 'Migration preview complete. Check output for details.'
        : 'Migration completed successfully!'
    );

  } catch (error: any) {
    vscode.window.showErrorMessage(`Migration failed: ${error.message}`);
  }
}

async function runBatchDiagramGeneration() {
  try {
    const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
    if (!workspaceFolder) {
      vscode.window.showErrorMessage('No workspace folder found');
      return;
    }
    
    await vscode.window.withProgress({
      location: vscode.ProgressLocation.Notification,
      title: 'Generating batch diagrams...',
      cancellable: false
    }, async (progress) => {
      progress.report({ message: 'Processing all .specly files and generating enhanced diagrams' });
      await execAsync('node scripts/generate-diagrams-batch.js', { cwd: workspaceFolder.uri.fsPath });
    });
    
    vscode.window.showInformationMessage('Batch diagram generation completed successfully');
    
  } catch (error: any) {
    vscode.window.showErrorMessage(`Batch diagram generation failed: ${error.message}`);
  }
}

export function deactivate() {
  console.log('SpecVerse v3.5 extension deactivated');
}