/**
 * VL Cascade Edit Tool
 *
 * When a service method signature changes or a component interface changes,
 * automatically finds all callers via ImpactAnalyzer, then uses LLM to fix
 * each affected file's section content, and validates the results.
 *
 * Workflow:
 *   1. analyzeServiceChange / analyzeComponentChange → get affected files
 *   2. For each file: read content, build targeted fix prompt, call LLM
 *   3. Apply fix using VLEditSection (section-level, surgical)
 *   4. Validate with VLValidate
 */

import { createProvider } from '../core/llm-provider.js';
import fs from 'fs/promises';
import path from 'path';

export function createVLCascadeEditTool(config, impactAnalyzer, toolRegistry) {
  let client;
  try { client = createProvider(config); } catch { client = null; }

  return {
    description: 'Cascade-edit all callers of a changed service method or component interface. Automatically finds affected files via impact analysis and applies LLM-assisted fixes to each. Use after changing a service method signature or component props.',
    parameters: {
      type: 'object',
      properties: {
        change_type: {
          type: 'string',
          enum: ['service_method', 'component_interface'],
          description: 'What was changed',
        },
        entity_id: {
          type: 'string',
          description: 'Service domain ID or component ID',
        },
        method_name: {
          type: 'string',
          description: 'Method name (for service_method change)',
        },
        change_description: {
          type: 'string',
          description: 'Human-readable description of the change, e.g. "getUser(userId) → getUser(userId, includeProfile: boolean)"',
        },
        dry_run: {
          type: 'boolean',
          description: 'If true, only report affected files without making changes',
        },
      },
      required: ['change_type', 'entity_id', 'change_description'],
    },
    execute: async (input) => {
      if (!impactAnalyzer) return { error: 'Impact analyzer not available' };
      if (!client)         return { error: 'API key not configured' };

      const { change_type, entity_id, method_name, change_description, dry_run } = input;

      // 1. Find affected files
      let impactReport;
      if (change_type === 'service_method') {
        impactReport = impactAnalyzer.analyzeServiceChange(entity_id, method_name || '');
      } else {
        impactReport = impactAnalyzer.analyzeComponentChange(entity_id, 'change_interface', method_name || '');
      }

      const affectedFiles = impactReport.affectedFiles || [];
      if (affectedFiles.length === 0) {
        return { result: `No callers found for ${entity_id}${method_name ? '.' + method_name : ''}. Nothing to update.` };
      }

      if (dry_run) {
        return {
          result: `Dry run — ${affectedFiles.length} file(s) would be updated:\n` +
            affectedFiles.map(f => `  - ${f}`).join('\n'),
        };
      }

      // 2. Fix each affected file in parallel (max 3 concurrent)
      const results = [];
      const CONCURRENCY = 3;

      for (let i = 0; i < affectedFiles.length; i += CONCURRENCY) {
        const batch = affectedFiles.slice(i, i + CONCURRENCY);
        const batchResults = await Promise.all(batch.map(relPath => fixFile(relPath, change_description, entity_id, method_name, config, client, toolRegistry)));
        results.push(...batchResults);
      }

      const fixed   = results.filter(r => r.ok);
      const failed  = results.filter(r => !r.ok);
      const summary = [
        `Cascade edit complete: ${fixed.length}/${affectedFiles.length} file(s) updated.`,
        ...fixed.map(r => `  ✓ ${r.file}`),
        ...failed.map(r => `  ✗ ${r.file}: ${r.error}`),
      ].join('\n');

      return { result: summary };
    },
  };
}

async function fixFile(relPath, changeDescription, entityId, methodName, config, client, toolRegistry) {
  try {
    const fullPath = path.resolve(config.workDir, relPath);
    let content;
    try {
      content = await fs.readFile(fullPath, 'utf-8');
    } catch {
      return { ok: false, file: relPath, error: 'File not found' };
    }

    // Ask LLM to produce only the changed section(s)
    const prompt = buildFixPrompt(relPath, content, changeDescription, entityId, methodName);

    const response = await client.messages.create({
      model: config.model,
      max_tokens: 4096,
      system: 'You are a VL language expert. You will receive a VL file and a description of a changed interface. Update only the sections of the file that reference the changed interface. Return the COMPLETE updated file content (all sections), preserving all sections that don\'t need changes. Return only the file content, no explanations.',
      messages: [{ role: 'user', content: prompt }],
    });

    const fixed = response.content[0]?.text?.trim() || '';
    if (!fixed) return { ok: false, file: relPath, error: 'LLM returned empty response' };

    // Write back
    await fs.mkdir(path.dirname(fullPath), { recursive: true });
    await fs.writeFile(fullPath, fixed, 'utf-8');

    // Auto-fix common syntax issues
    if (toolRegistry?.has('VLAutoFix')) {
      await toolRegistry.execute('VLAutoFix', { file_path: relPath });
    }

    // Validate
    let validationNote = '';
    if (toolRegistry?.has('VLValidate')) {
      const { result } = await toolRegistry.execute('VLValidate', { file_path: relPath });
      if (result && !result.includes('valid') && !result.includes('OK')) {
        validationNote = ` (validation: ${String(result).substring(0, 80)})`;
      }
    }

    return { ok: true, file: relPath + validationNote };
  } catch (err) {
    return { ok: false, file: relPath, error: err.message };
  }
}

function buildFixPrompt(relPath, content, changeDescription, entityId, methodName) {
  return `File: ${relPath}

Change that occurred:
${changeDescription}
${methodName ? `Entity: ${entityId}.${methodName}` : `Entity: ${entityId}`}

Current file content:
\`\`\`
${content}
\`\`\`

Update the file to reflect the above change. Only modify sections that reference the changed interface. Keep all other sections exactly as-is. Return the complete updated file content.`;
}
