const vscode = require('vscode');
const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
const { CONFIG } = require('../utils/config');

/**
 * AGENT Mode — Autonomous AI coding agent (Multi-Phase)
 * 
 * Flow:
 * 0. Workspace Discovery — scan files, detect project type
 * 1. AI Task Plan — generate dynamic task breakdown
 * 2. Deep File Reading — read up to 40 source files (60KB each), DOCX support
 * 3. Deep AI Analysis — full code audit with real file contents
 * 4. Fix Generation — optional auto-fix with truncation guards
 */
class AgentMode {
    constructor(aiService) {
        this.aiService = aiService;
        this.webviewProvider = null;
        this.todoProvider = null;
        this._fileChanges = [];
    }

    setWebviewProvider(provider) {
        this.webviewProvider = provider;
    }

    setTodoProvider(todoProvider) {
        this.todoProvider = todoProvider;
    }

    // ─── Helpers ────────────────────────────────────────────────────────

    _delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    _updateStep(stepIndex, status) {
        if (this.todoProvider) {
            this.todoProvider.updateTaskStatus('agent', stepIndex, status);
        }
    }

    sendStream(content) {
        if (this.webviewProvider && this.webviewProvider._view) {
            try {
                this.webviewProvider._view.webview.postMessage({
                    type: 'streamUpdate',
                    data: { content }
                });
            } catch (_) { /* ignore */ }
        }
    }

    sendThought(thought, type = 'thinking') {
        if (this.webviewProvider && this.webviewProvider._view) {
            try {
                this.webviewProvider._view.webview.postMessage({
                    type: 'streamThought',
                    data: { thought, type }
                });
            } catch (_) { /* ignore */ }
        }
    }

    sendProgress(mode, steps, completed, currentTask) {
        if (this.webviewProvider && this.webviewProvider._view) {
            try {
                this.webviewProvider._view.webview.postMessage({
                    type: 'todoProgress',
                    data: {
                        mode,
                        total: steps.length,
                        completed: Math.min(completed, steps.length),
                        steps: steps.map(s => typeof s === 'string' ? s : s.text),
                        currentTask
                    }
                });
            } catch (_) { /* ignore */ }
        }
    }

    // ─── Workspace Context (REAL) ───────────────────────────────────────

    async gatherWorkspaceContext(workspacePath) {
        const ctx = {
            path: workspacePath,
            name: path.basename(workspacePath),
            fileTree: '',
            packageInfo: null,
            projectType: 'unknown'
        };

        try {
            ctx.fileTree = this._buildFileTree(workspacePath, '', 0, 150);
        } catch (_) {
            ctx.fileTree = '(could not read workspace)';
        }

        try {
            const entries = fs.readdirSync(workspacePath);
            if (entries.includes('package.json')) {
                ctx.projectType = 'node';
                try {
                    const pkg = JSON.parse(fs.readFileSync(path.join(workspacePath, 'package.json'), 'utf8'));
                    ctx.packageInfo = {
                        name: pkg.name,
                        version: pkg.version,
                        dependencies: Object.keys(pkg.dependencies || {}).length,
                        devDependencies: Object.keys(pkg.devDependencies || {}).length,
                        scripts: Object.keys(pkg.scripts || {})
                    };
                } catch (_) { /* ignore */ }
            } else if (entries.includes('requirements.txt') || entries.includes('pyproject.toml')) {
                ctx.projectType = 'python';
            } else if (entries.includes('Cargo.toml')) {
                ctx.projectType = 'rust';
            } else if (entries.includes('go.mod')) {
                ctx.projectType = 'go';
            } else if (entries.includes('pom.xml') || entries.includes('build.gradle')) {
                ctx.projectType = 'java';
            }
        } catch (_) { /* ignore */ }

        return ctx;
    }

    _buildFileTree(dirPath, prefix, depth, maxEntries) {
        if (depth > 6 || maxEntries <= 0) return '';
        let result = '';
        let remaining = maxEntries;
        const skip = new Set(['.git', 'node_modules', '__pycache__', 'dist', 'build', '.next', 'venv', '.venv', '.cache']);

        try {
            const entries = fs.readdirSync(dirPath, { withFileTypes: true })
                .filter(e => !e.name.startsWith('.') && !skip.has(e.name))
                .sort((a, b) => a.isDirectory() === b.isDirectory() ? a.name.localeCompare(b.name) : a.isDirectory() ? -1 : 1);

            for (const entry of entries) {
                if (remaining <= 0) {
                    result += `${prefix}... (truncated)\n`;
                    break;
                }
                if (entry.isDirectory()) {
                    result += `${prefix}${entry.name}/\n`;
                    remaining--;
                    const sub = this._buildFileTree(path.join(dirPath, entry.name), prefix + '  ', depth + 1, Math.min(remaining, 20));
                    result += sub;
                    remaining -= sub.split('\n').filter(l => l.trim()).length;
                } else {
                    result += `${prefix}${entry.name}\n`;
                    remaining--;
                }
            }
        } catch (_) { /* ignore */ }

        return result;
    }

    async getActiveFileContext() {
        try {
            const editor = vscode.window.activeTextEditor;
            if (!editor) return null;
            const doc = editor.document;
            const sel = editor.selection;
            return {
                fileName: path.basename(doc.fileName),
                fullPath: doc.fileName,
                language: doc.languageId,
                lineCount: doc.lineCount,
                hasSelection: !sel.isEmpty,
                selectedText: !sel.isEmpty ? doc.getText(sel).substring(0, 3000) : null,
                contentSnippet: doc.getText().substring(0, 8000)
            };
        } catch (_) {
            return null;
        }
    }

    // ─── DOCX Text Extractor (pure JS, no external libs) ───────────────

    _readDocxText(filePath) {
        try {
            const buf = fs.readFileSync(filePath);
            if (buf.length < 30 || buf[0] !== 0x50 || buf[1] !== 0x4B) return null;

            let offset = 0;
            while (offset < buf.length - 30) {
                if (buf[offset] === 0x50 && buf[offset + 1] === 0x4B && buf[offset + 2] === 0x03 && buf[offset + 3] === 0x04) {
                    const compressionMethod = buf.readUInt16LE(offset + 8);
                    const compressedSize = buf.readUInt32LE(offset + 18);
                    const nameLen = buf.readUInt16LE(offset + 26);
                    const extraLen = buf.readUInt16LE(offset + 28);
                    const name = buf.toString('utf8', offset + 30, offset + 30 + nameLen);
                    const dataStart = offset + 30 + nameLen + extraLen;

                    if (name === 'word/document.xml' && dataStart + compressedSize <= buf.length) {
                        let xmlBuf;
                        if (compressionMethod === 0) {
                            xmlBuf = buf.slice(dataStart, dataStart + compressedSize);
                        } else if (compressionMethod === 8) {
                            xmlBuf = zlib.inflateRawSync(buf.slice(dataStart, dataStart + compressedSize));
                        } else {
                            return null;
                        }

                        const xml = xmlBuf.toString('utf8');
                        let text = '';
                        const lines = xml.split(/<w:p[\s>]/);
                        for (const line of lines) {
                            const parts = [];
                            const tRegex = /<w:t(?:\s[^>]*)?>([^<]*)<\/w:t>/g;
                            let m;
                            while ((m = tRegex.exec(line)) !== null) {
                                parts.push(m[1]);
                            }
                            if (parts.length > 0) {
                                text += parts.join('') + '\n';
                            }
                        }
                        return text.trim() || null;
                    }
                    offset = dataStart + (compressedSize > 0 ? compressedSize : 1);
                } else {
                    offset++;
                }
            }
            return null;
        } catch (e) {
            console.warn('DOCX read failed for ' + filePath + ': ' + e.message);
            return null;
        }
    }

    // ─── AI-Driven Task Planning ────────────────────────────────────────

    async getAITaskPlan(message, workspaceCtx) {
        const planPrompt = `You are a task planner. Based on the user's request, create a focused task plan.

RULES:
- Return ONLY a valid JSON array of short task descriptions (strings)
- Each task should be 3-8 words, action-oriented
- Simple requests: 2-3 tasks
- Medium requests: 4-6 tasks
- Complex analysis/refactoring: 6-10 tasks
- Do NOT include generic filler tasks
- Tasks should be specific to what needs to be done

PROJECT: ${workspaceCtx ? workspaceCtx.name + ' (' + workspaceCtx.projectType + ')' : 'Unknown'}

USER REQUEST: "${message}"

Return ONLY the JSON array, nothing else. Example: ["Scan project dependencies","Analyze authentication flow","Review database queries","Check error handling patterns","Generate improvement report"]`;

        try {
            const planResponse = await this.aiService.processMessage(planPrompt, {
                mode: 'ask',
                skipContext: true
            });
            const content = planResponse.content || planResponse.response || '';
            const jsonMatch = content.match(/\[[\s\S]*?\]/);
            if (jsonMatch) {
                const parsed = JSON.parse(jsonMatch[0]);
                if (Array.isArray(parsed) && parsed.length >= 2 && parsed.length <= 15) {
                    return parsed.map(t => String(t).trim()).filter(t => t.length > 0);
                }
            }
        } catch (err) {
            console.warn('Agent plan parsing failed:', err.message);
        }

        // Smart fallback plan based on intent
        const fallbackIntent = this.detectIntent(message);
        if (fallbackIntent === 'audit-and-fix') {
            return ['Scanning project structure', 'Reading source files', 'Analyzing code quality', 'Identifying security gaps', 'Checking error handling', 'Reviewing dependencies', 'Generating fix plan', 'Applying code fixes', 'Verifying changes', 'Compiling final report'];
        } else if (fallbackIntent === 'fix') {
            return ['Scanning project structure', 'Reading source files', 'Identifying issues', 'Planning fixes', 'Generating code changes', 'Applying modifications', 'Verifying results'];
        }
        return ['Scanning project structure', 'Reading source files', 'Analyzing code patterns', 'Checking dependencies', 'Identifying gaps', 'Reviewing security', 'Generating audit report'];
    }

    // ─── Intent Detection ──────────────────────────────────────────────

    detectIntent(message) {
        const fixWords = /\b(fix|repair|patch|resolve|implement|create|update|refactor|improve|harden|secure|add|write|generate|setup|install|configure|migrate|upgrade|restructure|rewrite|optimize|clean)\b/i;
        const auditWords = /\b(audit|analyze|review|check|inspect|scan|assess|evaluate|examine|investigate|deep\s*dive|find|identify|list|report|document|gaps?|issues?|problems?|vulnerabilit)/i;
        const wantsFix = fixWords.test(message);
        const wantsAudit = auditWords.test(message);
        if (wantsFix && wantsAudit) return 'audit-and-fix';
        if (wantsFix) return 'fix';
        return 'audit';
    }

    // ─── Deep File Reader (async to prevent UI freeze) ──────────────────

    async readRelevantProjectFiles(workspacePath, maxFiles, maxBytesPerFile, onFileRead) {
        maxFiles = maxFiles || 40;
        maxBytesPerFile = maxBytesPerFile || 60000;
        const results = [];
        const skip = new Set(['.git', 'node_modules', '__pycache__', 'dist', 'build', '.next', 'venv', '.venv', '.cache', 'coverage', '.nyc_output', 'logs', '.svn', 'vendor', 'bower_components', '.tox', 'htmlcov', 'egg-info']);
        const codeExts = new Set(['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs', '.py', '.java', '.go', '.rs', '.rb', '.php', '.vue', '.svelte', '.html', '.css', '.scss', '.json', '.yaml', '.yml', '.toml', '.md', '.sql', '.sh', '.bat', '.ps1', '.env', '.cfg', '.ini', '.xml', '.conf', '.docx']);
        const priorityPatterns = [
            /^(main|index|app|server|config|setup|preload|security|auth)\./i,
            /^(package|tsconfig|webpack|vite|rollup|babel|eslint|jest|mocha|karma)\./i,
            /^(Dockerfile|docker-compose|Makefile|Caddyfile|nginx\.conf|ecosystem\.config)$/i,
            /(security|auth|middleware|validator|sanitizer|crypto|permission|access)/i,
            /^\.env/i,
            /^(README|CHANGELOG)/i,
            /(schema|migration|model|route|controller|handler|service|database|db)\./i,
            /\.(sql|prisma|graphql|gql)$/i,
            /(requirements|Gemfile|Pipfile|pyproject|Cargo|go\.mod|go\.sum)/i,
            /\.docx$/i
        ];

        const allFiles = [];
        const scanDir = (dir, depth) => {
            if (depth > 7) return;
            try {
                const entries = fs.readdirSync(dir, { withFileTypes: true });
                for (const entry of entries) {
                    if (entry.name.startsWith('.') && !entry.name.startsWith('.env')) continue;
                    if (skip.has(entry.name)) continue;
                    const fullPath = path.join(dir, entry.name);
                    if (entry.isDirectory()) {
                        scanDir(fullPath, depth + 1);
                    } else {
                        const ext = path.extname(entry.name).toLowerCase();
                        if (codeExts.has(ext) || entry.name.startsWith('.env')) {
                            const relPath = path.relative(workspacePath, fullPath);
                            const isPriority = priorityPatterns.some(p => p.test(entry.name) || p.test(relPath));
                            try {
                                const stat = fs.statSync(fullPath);
                                if (stat.size > 0 && stat.size < 2000000) {
                                    allFiles.push({ fullPath, relPath, name: entry.name, size: stat.size, isPriority, ext });
                                }
                            } catch (_) { /* ignore */ }
                        }
                    }
                }
            } catch (_) { /* ignore */ }
        };

        scanDir(workspacePath, 0);

        // Sort: priority first, then by size (smaller first)
        allFiles.sort((a, b) => {
            if (a.isPriority !== b.isPriority) return a.isPriority ? -1 : 1;
            return a.size - b.size;
        });

        const filesToRead = allFiles.slice(0, maxFiles);
        for (let i = 0; i < filesToRead.length; i++) {
            const file = filesToRead[i];
            try {
                // Yield event loop every 3 files to prevent UI freeze
                if (i > 0 && i % 3 === 0) {
                    await new Promise(r => setTimeout(r, 0));
                }

                // Special handling for DOCX files
                if (file.ext === '.docx') {
                    const docxText = this._readDocxText(file.fullPath);
                    if (docxText) {
                        results.push({
                            path: file.relPath,
                            name: file.name,
                            content: docxText.substring(0, maxBytesPerFile),
                            truncated: docxText.length > maxBytesPerFile,
                            damaged: false,
                            size: file.size,
                            isDocument: true
                        });
                        if (onFileRead) onFileRead(file.relPath, i, filesToRead.length);
                    }
                    continue;
                }

                const content = fs.readFileSync(file.fullPath, 'utf8');

                // Detect previously damaged files before sending to AI
                const damagePatterns = [
                    /\/\/\s*\.{2,}\s*rest of/i,
                    /remains exactly the same/i,
                    /remaining code unchanged/i,
                    /\/\/\s*\.{2,}\s*existing code/i
                ];
                const isDamaged = damagePatterns.some(p => p.test(content));
                if (isDamaged) {
                    console.warn('⚠️ PRE-READ: ' + file.relPath + ' appears to be previously truncated/damaged');
                }

                // Smart reading for large files: first portion + last portion
                let fileContent;
                let wasSplitRead = false;
                if (content.length > maxBytesPerFile) {
                    const firstPortion = Math.ceil(maxBytesPerFile * 0.65);
                    const lastPortion = maxBytesPerFile - firstPortion - 200;
                    const firstPart = content.substring(0, firstPortion);
                    const lastPart = content.substring(content.length - lastPortion);
                    fileContent = firstPart + '\n\n// ═══ TRUNCATED: ' + (content.length - firstPortion - lastPortion) + ' bytes omitted (file is ' + content.length + ' bytes total) ═══\n// ═══ Showing last ' + lastPortion + ' bytes below ═══\n\n' + lastPart;
                    wasSplitRead = true;
                } else {
                    fileContent = content;
                }

                results.push({
                    path: file.relPath,
                    name: file.name,
                    content: fileContent,
                    truncated: content.length > maxBytesPerFile,
                    splitRead: wasSplitRead,
                    damaged: isDamaged,
                    size: file.size
                });

                if (onFileRead) onFileRead(file.relPath, i, filesToRead.length);
            } catch (_) { /* ignore */ }
        }
        return results;
    }

    // ─── Truncation Detection Patterns ──────────────────────────────────

    _isTruncatedContent(content, originalContent) {
        const truncationPatterns = [
            /\/\/\s*\.{2,}\s*rest of/i,
            /\/\/\s*\.{2,}\s*existing code/i,
            /\/\/\s*\.{2,}\s*remaining/i,
            /\/\/\s*\.{2,}\s*same as/i,
            /\/\/\s*\.{2,}\s*unchanged/i,
            /\/\/\s*\.{2,}\s*continues/i,
            /\/\/\s*\.{2,}\s*omitted/i,
            /\/\/\s*\.{2,}\s*truncated/i,
            /\/\*\s*\.{2,}\s*rest of/i,
            /#\s*\.{2,}\s*rest of/i,
            /rest of the file remains/i,
            /remains exactly the same/i,
            /remaining code unchanged/i,
            /previous code unchanged/i,
            /code continues as before/i,
            /same as (?:before|above|original)/i,
            /CONTENT TRUNCATED/i,
            /file content unchanged/i,
            /keep existing code/i,
            /no changes below/i,
            /\/\/\s*\[\.{2,}\]/i,
            /\/\/\s*<rest of/i,
            /\/\/\s*\(rest of/i
        ];
        for (const p of truncationPatterns) {
            if (p.test(content)) return true;
        }

        if (originalContent && originalContent.length > 200) {
            if (content.length < originalContent.length * 0.6) return true;
            const origLines = originalContent.split('\n').length;
            const newLines = content.split('\n').length;
            if (origLines > 20 && newLines < origLines * 0.5) return true;
        }

        if (originalContent) {
            const hadExports = /module\.exports|export\s+(default|{|class|function|const|let|var)/i.test(originalContent);
            const hasExports = /module\.exports|export\s+(default|{|class|function|const|let|var)/i.test(content);
            if (hadExports && !hasExports) return true;
        }

        return false;
    }

    // ─── Apply Code Changes from AI Response ────────────────────────────

    async applyCodeChanges(aiResponse, workspacePath) {
        const applied = [];
        this._fileChanges = this._fileChanges || [];
        const fixPattern = /```fix\s*\nFILE:\s*(.+?)\n---CONTENT---\n([\s\S]*?)---END---\s*\n```/g;
        let match;

        while ((match = fixPattern.exec(aiResponse)) !== null) {
            const relPath = match[1].trim();
            const content = match[2];
            const fullPath = path.join(workspacePath, relPath);

            try {
                const uri = vscode.Uri.file(fullPath);

                if (fs.existsSync(fullPath)) {
                    const originalContent = fs.readFileSync(fullPath, 'utf8');

                    // TRUNCATION GUARD: reject content that looks truncated
                    if (this._isTruncatedContent(content, originalContent)) {
                        console.warn('⚠️ TRUNCATION DETECTED in ' + relPath + ' — skipping to protect file');
                        this.sendThought('⚠️ Skipped ' + relPath + ' — AI output was truncated', 'warning');
                        continue;
                    }

                    const doc = await vscode.workspace.openTextDocument(uri);
                    const edit = new vscode.WorkspaceEdit();
                    const fullRange = new vscode.Range(doc.positionAt(0), doc.positionAt(doc.getText().length));
                    edit.replace(uri, fullRange, content);
                    const ok = await vscode.workspace.applyEdit(edit);

                    if (ok) {
                        await doc.save();
                        const ext = path.extname(relPath).toLowerCase();
                        const oldLines = originalContent.split('\n').length;
                        const newLines = content.split('\n').length;

                        // Verify JSON files parse correctly
                        if (ext === '.json') {
                            try {
                                JSON.parse(content);
                            } catch (jsonErr) {
                                const restoreEdit = new vscode.WorkspaceEdit();
                                const restoreRange = new vscode.Range(doc.positionAt(0), doc.positionAt(content.length));
                                restoreEdit.replace(uri, restoreRange, originalContent);
                                await vscode.workspace.applyEdit(restoreEdit);
                                await doc.save();
                                console.warn('⚠️ REVERTED ' + relPath + ' — invalid JSON: ' + jsonErr.message);
                                this.sendThought('⚠️ Reverted ' + relPath + ' — AI output was invalid JSON', 'warning');
                                continue;
                            }
                        }

                        // Verify line count sanity — catch drastic reductions
                        if (oldLines > 20 && newLines < oldLines * 0.5) {
                            const restoreEdit = new vscode.WorkspaceEdit();
                            const restoreRange = new vscode.Range(doc.positionAt(0), doc.positionAt(content.length));
                            restoreEdit.replace(uri, restoreRange, originalContent);
                            await vscode.workspace.applyEdit(restoreEdit);
                            await doc.save();
                            console.warn('⚠️ REVERTED ' + relPath + ' — too much code lost: ' + oldLines + '→' + newLines + ' lines');
                            this.sendThought('⚠️ Reverted ' + relPath + ' — too much code lost (' + oldLines + '→' + newLines + ' lines)', 'warning');
                            continue;
                        }

                        applied.push(relPath);
                        this._fileChanges.push({
                            name: relPath,
                            added: Math.max(0, newLines - oldLines),
                            removed: Math.max(0, oldLines - newLines)
                        });
                        console.log('✅ Agent applied fix:', relPath, '+' + Math.max(0, newLines - oldLines), '-' + Math.max(0, oldLines - newLines));
                    }
                } else {
                    const dir = path.dirname(fullPath);
                    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
                    fs.writeFileSync(fullPath, content, 'utf8');
                    const newLines = content.split('\n').length;
                    this._fileChanges.push({ name: relPath + ' (new)', added: newLines, removed: 0 });
                    applied.push(relPath + ' (new)');
                    console.log('✅ Agent created file:', relPath);
                }
            } catch (err) {
                console.warn('⚠️ Agent could not write ' + relPath + ':', err.message);
            }
        }

        // Send file status update to webview
        if (this.webviewProvider && this.webviewProvider._view) {
            this.webviewProvider._view.webview.postMessage({
                type: 'updateFileStatus',
                data: this._fileChanges
            });
        }
        return applied;
    }

    // ─── Main Processing (Multi-Phase Real Execution) ────────────────────

    async processMessage(message, context) {
        const startTime = Date.now();
        this._fileChanges = [];

        try {
            console.log('🤖 AGENT MODE: Processing message (multi-phase)');

            // ── Phase 0: Workspace Discovery ──
            this.sendThought('Scanning workspace files...', 'reading');

            const workspaceFolders = vscode.workspace.workspaceFolders;
            let workspacePath = '';
            if (workspaceFolders && workspaceFolders[0] && workspaceFolders[0].uri) {
                workspacePath = workspaceFolders[0].uri.fsPath;
            }

            let workspaceCtx = null;
            if (workspacePath) {
                this.sendThought('Reading project: ' + path.basename(workspacePath), 'reading');
                workspaceCtx = await this.gatherWorkspaceContext(workspacePath);
                if (workspaceCtx) {
                    this.sendThought('Detected ' + workspaceCtx.projectType + ' project — ' + workspaceCtx.fileTree.split('\n').filter(l => l.trim()).length + ' files mapped', 'reading');
                    if (workspaceCtx.packageInfo) {
                        this.sendThought('Dependencies: ' + workspaceCtx.packageInfo.dependencies + ' prod, ' + workspaceCtx.packageInfo.devDependencies + ' dev', 'reading');
                    }
                }
            }

            const activeFile = await this.getActiveFileContext();
            if (activeFile) {
                this.sendThought('Active file: ' + activeFile.fileName + ' (' + activeFile.lineCount + ' lines)', 'reading');
            }

            const intent = this.detectIntent(message);

            // ── Phase 1: AI Task Plan ──
            this.sendThought('Creating AI task plan...', 'analyzing');
            const taskPlan = await this.getAITaskPlan(message, workspaceCtx);
            const steps = taskPlan.map(text => ({ text }));
            if (this.todoProvider) {
                this.todoProvider.setAITaskPlan('agent', steps);
            }
            this.sendProgress('agent', steps, 0, steps[0].text);

            let currentStep = 0;
            const markStep = (status) => {
                if (currentStep < steps.length) {
                    this._updateStep(currentStep, status);
                    if (status === 'in-progress') {
                        this.sendThought(steps[currentStep].text + '...', 'analyzing');
                        this.sendProgress('agent', steps, currentStep, steps[currentStep].text);
                    } else if (status === 'completed') {
                        this.sendProgress('agent', steps, currentStep + 1, steps[currentStep].text);
                        currentStep++;
                    }
                }
            };

            // ── Phase 2: Deep File Reading ──
            markStep('in-progress');
            let projectFiles = [];
            if (workspacePath) {
                this.sendThought('Reading project source files for deep analysis...', 'reading');
                const stepsForReading = Math.min(Math.ceil(steps.length * 0.3), steps.length - 2);
                let filesReadCount = 0;

                projectFiles = await this.readRelevantProjectFiles(workspacePath, 40, 60000, (filePath, idx, total) => {
                    filesReadCount++;
                    this.sendThought('Reading: ' + filePath, 'reading');
                    const filesPerStep = Math.max(1, Math.ceil(total / stepsForReading));
                    if (filesReadCount > 0 && filesReadCount % filesPerStep === 0 && currentStep < stepsForReading) {
                        markStep('completed');
                        if (currentStep < steps.length) markStep('in-progress');
                    }
                });

                const totalKB = (projectFiles.reduce((s, f) => s + f.content.length, 0) / 1024).toFixed(0);
                this.sendThought('Read ' + projectFiles.length + ' source files (' + totalKB + ' KB of code)', 'reading');
            }
            markStep('completed');

            // ── Phase 3: Deep AI Analysis ──
            markStep('in-progress');
            this.sendThought('Running deep AI analysis on ' + projectFiles.length + ' source files...', 'thinking');

            const analysisPrompt = this._buildDeepAnalysisPrompt(message, workspaceCtx, projectFiles, activeFile, intent);

            const analysisPhaseEnd = Math.max(currentStep + 1, Math.ceil(steps.length * 0.7));
            const progressTimer = setInterval(() => {
                if (currentStep < analysisPhaseEnd && currentStep < steps.length - 1) {
                    markStep('completed');
                    markStep('in-progress');
                }
            }, 8000);

            let analysisResponse;
            try {
                analysisResponse = await this.aiService.processMessage(analysisPrompt, {
                    mode: 'agent',
                    skipContext: true,
                    rawMessage: message,
                    agentPrompt: analysisPrompt,
                    agentTaskType: (intent === 'fix' || intent === 'audit-and-fix') ? 'debugging' : 'analysis',
                    workspace: workspaceCtx ? {
                        name: workspaceCtx.name,
                        path: workspaceCtx.path,
                        projectType: workspaceCtx.projectType,
                        fileTree: workspaceCtx.fileTree
                    } : null,
                    activeFile: activeFile || null
                });
            } catch (aiErr) {
                clearInterval(progressTimer);
                console.error('🤖 AGENT analysis AI call failed:', aiErr.message);
                if (this.todoProvider) this.todoProvider.completeAllAITasks('agent');
                return {
                    success: false,
                    content: '## ⚠️ Agent Error\n\nCould not reach Vigthoria AI: ' + aiErr.message + '\n\nPlease check your connection and try again.',
                    actions: [],
                    metadata: { mode: 'agent', error: true, timestamp: new Date().toISOString() }
                };
            }
            clearInterval(progressTimer);

            const analysisContent = analysisResponse.content || analysisResponse.response || '';

            // Detect if the AI service returned an error response instead of real analysis
            if (analysisContent.includes('Could not reach Vigthoria AI') || analysisContent.includes('fetch failed') || (analysisResponse.metadata && analysisResponse.metadata.offline)) {
                console.error('🤖 AGENT: AI returned error response instead of analysis');
                if (this.todoProvider) this.todoProvider.completeAllAITasks('agent');
                return {
                    success: false,
                    content: '## ⚠️ Agent Error\n\nCould not connect to Vigthoria AI servers after multiple retries.\n\n**Possible causes:**\n- Internet connection issue\n- Firewall or antivirus blocking the connection\n- Server temporarily unavailable\n\n**Try:**\n1. Check your internet connection\n2. Restart Vigthoria Code\n3. Try again in a few seconds',
                    actions: [],
                    metadata: { mode: 'agent', error: true, timestamp: new Date().toISOString() }
                };
            }

            // Complete all analysis phase steps
            while (currentStep < analysisPhaseEnd && currentStep < steps.length) {
                markStep('completed');
            }

            // ── Phase 4: Implementation / Fix Generation ──
            let fixContent = '';
            let appliedFixes = [];
            if (intent === 'fix' || intent === 'audit-and-fix') {
                if (currentStep < steps.length) markStep('in-progress');
                this.sendThought('Generating code fixes based on analysis...', 'generating');

                const fixPrompt = this._buildFixPrompt(message, analysisContent, projectFiles, workspaceCtx);
                try {
                    const fixResponse = await this.aiService.processMessage(fixPrompt, {
                        mode: 'agent',
                        skipContext: true
                    });
                    fixContent = fixResponse.content || fixResponse.response || '';

                    if (workspacePath && fixContent) {
                        this.sendThought('Applying code changes...', 'generating');
                        appliedFixes = await this.applyCodeChanges(fixContent, workspacePath);
                        if (appliedFixes.length > 0) {
                            for (const f of appliedFixes) {
                                this.sendThought('✅ Modified: ' + f, 'complete');
                            }
                            this.sendThought('Applied ' + appliedFixes.length + ' file changes', 'complete');
                            for (let i = 0; i < appliedFixes.length && currentStep < steps.length; i++) {
                                markStep('completed');
                                if (currentStep < steps.length) markStep('in-progress');
                            }
                        } else {
                            this.sendThought('Analysis complete — no auto-fixable changes detected', 'complete');
                        }
                    }
                } catch (fixErr) {
                    console.warn('⚠️ Agent fix generation failed:', fixErr.message);
                    fixContent = '\n\n> ⚠️ Fix generation encountered an error: ' + fixErr.message;
                }
            }

            // ── Finalize ──
            const hasActualFixes = appliedFixes.length > 0;
            const hadErrors = !analysisContent || analysisContent.includes('Could not reach') || analysisContent.includes('fetch failed');
            if (hasActualFixes || (!hadErrors && analysisContent.length > 100)) {
                while (currentStep < steps.length) {
                    markStep('completed');
                }
                this.sendProgress('agent', steps, steps.length, 'All tasks completed');
            } else {
                this.sendProgress('agent', steps, currentStep, 'Completed with issues');
                this.sendThought('⚠️ Some tasks could not be completed', 'warning');
            }

            if (this.todoProvider) this.todoProvider.completeAllAITasks('agent');

            const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
            this.sendThought('Complete (' + elapsed + 's) — ' + projectFiles.length + ' files analyzed', 'complete');

            let finalContent = analysisContent;
            finalContent = this._stripChatbotEndings(finalContent);

            if (fixContent && appliedFixes.length > 0) {
                finalContent += '\n\n---\n\n## 🔧 Applied Fixes\n\n' + fixContent;
                finalContent += '\n\n### Files Modified:\n' + appliedFixes.map(f => '- ✅ ' + f).join('\n');
            } else if (fixContent) {
                finalContent += '\n\n---\n\n## 🔧 Suggested Fixes\n\n' + fixContent;
            }

            finalContent += '\n\n---\n*Agent analyzed ' + projectFiles.length + ' files (' + (projectFiles.reduce((s, f) => s + f.content.length, 0) / 1024).toFixed(0) + ' KB) in ' + elapsed + 's*';

            return {
                success: true,
                content: finalContent,
                actions: [],
                metadata: {
                    mode: 'agent',
                    source: (analysisResponse.metadata && analysisResponse.metadata.source) || 'multi-phase-agent',
                    processingTime: elapsed + 's',
                    model: (analysisResponse.metadata && analysisResponse.metadata.model) || 'unknown',
                    tasksCompleted: steps.length,
                    filesAnalyzed: projectFiles.length,
                    filesModified: appliedFixes.length,
                    intent,
                    timestamp: new Date().toISOString()
                }
            };

        } catch (error) {
            console.error('❌ AGENT MODE Error:', error);
            if (this.todoProvider) this.todoProvider.completeAllAITasks('agent');
            return {
                success: false,
                content: '## ❌ Agent Error\n\n' + (error.message || 'Unknown error') + '. Please try again.',
                actions: [],
                metadata: { mode: 'agent', error: true, timestamp: new Date().toISOString() }
            };
        }
    }

    // ─── Strip Chatbot Endings (post-processing) ────────────────────────

    _stripChatbotEndings(text) {
        if (!text || typeof text !== 'string') return text;
        const patterns = [
            /\n+(?:Let me know|Feel free to (?:ask|reach|let)|(?:Would|Do) you (?:like|want|need)|If you (?:need|want|have|would)|I(?:'m| am) (?:here|happy|ready) to|Don't hesitate|Please (?:let me know|feel free)|Shall I|Want me to|I can (?:also|help|assist|elaborate)|Need (?:any|more|further)|Happy to (?:help|assist|elaborate)).*$/gi,
            /\n+---\n*$/,
            /\n*By addressing these (?:gaps|issues|recommendations).*$/gi,
            /\n*(?:In summary|In conclusion|To summarize|Overall),?\s+(?:by|the|this|these|addressing).*$/gi
        ];
        let cleaned = text;
        for (const pattern of patterns) {
            cleaned = cleaned.replace(pattern, '');
        }
        return cleaned.trimEnd();
    }

    // ─── Deep Analysis Prompt (with real file contents) ─────────────────

    _buildDeepAnalysisPrompt(message, workspaceCtx, projectFiles, activeFile, intent) {
        const truncatedFiles = projectFiles.filter(f => f.truncated || f.splitRead);
        const docFiles = projectFiles.filter(f => f.isDocument);

        let prompt = '# SYSTEM: Vigthoria Agent — Autonomous Code Analysis Engine\n\n';
        prompt += 'You are an expert code auditor embedded inside Vigthoria Code IDE.\n';
        prompt += 'You have been given source code from the user\'s project below.\n';
        prompt += 'Your job is to analyze THIS EXACT CODE — not generic templates.\n\n';

        if (truncatedFiles.length > 0) {
            prompt += '## ⚠️ IMPORTANT: PARTIAL FILE CONTENT WARNING\n';
            prompt += 'The following files were TOO LARGE to include in full and have been TRUNCATED:\n';
            for (const f of truncatedFiles) {
                const pct = Math.round((f.content.length / f.size) * 100);
                prompt += '- **' + f.path + '**: showing ~' + pct + '% of ' + f.size + ' bytes' + (f.splitRead ? ' (first + last portions)' : '') + '\n';
            }
            prompt += '\n**Because of truncation, you MUST:**\n';
            prompt += '- NEVER claim something \'does not exist\' or \'is missing\' if it could be in the truncated portion of a file\n';
            prompt += '- When a file is truncated, say \'not visible in the provided portion\' instead of \'does not exist\'\n';
            prompt += '- For large files, explicitly note what sections you CAN see vs what might be hidden\n';
            prompt += '- If routes, functions, or features appear in the visible portion, analyze them. Do not speculate about what\'s in hidden portions.\n\n';
        }

        if (docFiles.length > 0) {
            prompt += '## 📄 DOCUMENT FILES INCLUDED\n';
            prompt += 'The following document files (DOCX/etc) have been converted to plain text for your analysis:\n';
            for (const f of docFiles) {
                prompt += '- **' + f.path + '** (extracted text from ' + path.extname(f.name).toUpperCase().replace('.', '') + ' document)\n';
            }
            prompt += 'When the user asks to compare a document against the code, carefully cross-reference the document content against the actual implementation.\n\n';
        }

        prompt += '## CRITICAL RULES (MUST FOLLOW)\n';
        prompt += '- ONLY reference files, functions, variables, and line patterns that EXIST in the source code shown below\n';
        prompt += '- NEVER invent file names, function names, or code that isn\'t shown\n';
        prompt += '- NEVER give generic advice like \'add error handling\' without citing the EXACT code that needs it\n';
        prompt += '- NEVER use placeholder examples like \'api.example.com\' — use the ACTUAL values from the code\n';
        prompt += '- NEVER suggest adding libraries that aren\'t already in package.json unless absolutely necessary\n';
        prompt += '- When you cite an issue, QUOTE the exact problematic code from the source files\n';
        prompt += '- NEVER end your response with a question or \'let me know\' or \'would you like\'\n';
        prompt += '- Your response IS the complete deliverable. Do not offer to do more.\n';
        prompt += '- When files are truncated, NEVER claim a feature is \'missing\' — say \'not found in the visible portion\'\n\n';

        if (workspaceCtx) {
            prompt += '## Project Overview\n';
            prompt += '- **Project:** ' + workspaceCtx.name + '\n';
            prompt += '- **Type:** ' + workspaceCtx.projectType + '\n';
            prompt += '- **Path:** ' + workspaceCtx.path + '\n';
            if (workspaceCtx.packageInfo) {
                const pkg = workspaceCtx.packageInfo;
                prompt += '- **Package:** ' + pkg.name + '@' + pkg.version + '\n';
                prompt += '- **Dependencies:** ' + pkg.dependencies + ' prod, ' + pkg.devDependencies + ' dev\n';
                if (pkg.scripts.length > 0) prompt += '- **Scripts:** ' + pkg.scripts.join(', ') + '\n';
            }
            if (workspaceCtx.fileTree) {
                prompt += '\n### File Structure:\n```\n' + workspaceCtx.fileTree + '```\n';
            }
        }

        if (projectFiles.length > 0) {
            prompt += '\n## ACTUAL SOURCE CODE (' + projectFiles.length + ' files)\n';
            prompt += 'The following is the REAL content of the project files. Analyze THIS code.\n';
            for (const file of projectFiles) {
                const ext = file.isDocument ? 'text' : (path.extname(file.name).replace('.', '') || 'text');
                let label = file.path + ' (' + file.size + ' bytes';
                if (file.truncated && file.splitRead) label += ', ⚠️ TRUNCATED — showing first+last portions only';
                else if (file.truncated) label += ', ⚠️ TRUNCATED';
                if (file.isDocument) label += ', extracted text from document';
                if (file.damaged) label += ', ⚠️ PREVIOUSLY DAMAGED — DO NOT include in fix blocks';
                label += ')';
                prompt += '\n### 📄 ' + label + '\n';
                prompt += '```' + ext + '\n' + file.content + '\n```\n';
            }
        }

        if (activeFile) {
            prompt += '\n## Currently Open File: ' + activeFile.fileName + '\n';
            if (activeFile.hasSelection && activeFile.selectedText) {
                prompt += '\n### Selected Code:\n```' + activeFile.language + '\n' + activeFile.selectedText + '\n```\n';
            } else if (activeFile.contentSnippet) {
                prompt += '\n### Content:\n```' + activeFile.language + '\n' + activeFile.contentSnippet + '\n```\n';
            }
        }

        prompt += '\n## User Request\n"' + message + '"\n';

        prompt += '\n## RESPONSE FORMAT (MANDATORY)\n';
        prompt += 'Structure your response EXACTLY like this:\n\n';
        prompt += '### 1. Executive Summary\n';
        prompt += 'One paragraph describing what the project does and its overall health.\n\n';
        prompt += '### 2. Critical Issues (must fix)\n';
        prompt += 'For each issue:\n';
        prompt += '- **File:** `exact/path/to/file.ext`\n';
        prompt += '- **Problem:** Quote the exact problematic code and explain what\'s wrong\n';
        prompt += '- **Impact:** What breaks or is at risk\n';
        prompt += '- **Fix:** Show the exact corrected code\n\n';
        prompt += '### 3. Important Improvements\n';
        prompt += 'Same format as above for high-priority improvements.\n\n';
        prompt += '### 4. Minor Improvements\n';
        prompt += 'Brief list of nice-to-have improvements.\n\n';

        if (intent === 'fix' || intent === 'audit-and-fix') {
            prompt += '### 5. APPLIED CHANGES\n';
            prompt += 'For EACH file you want to fix, output the COMPLETE corrected version:\n\n';
            prompt += '```fix\nFILE: relative/path/to/file.ext\n---CONTENT---\n<entire corrected file content here — EVERY LINE>\n---END---\n```\n\n';
            prompt += 'CRITICAL RULES FOR FIX BLOCKS:\n';
            prompt += '- The system will OVERWRITE the entire file with your content. Include EVERY LINE.\n';
            prompt += '- NEVER use \'// ... rest of the file ...\' or \'// remaining code unchanged\' — these DESTROY the file.\n';
            prompt += '- If a file is too long to include completely, DO NOT include it. Skip it.\n';
            prompt += '- For .json files: NEVER add // comments — JSON does not support comments.\n';
            prompt += '- Every module.exports/export from the original MUST appear in your output.\n';
            prompt += '- Only include files that actually need changes. Preserve ALL working code.\n';
        }

        prompt += '\nDo NOT end with questions, offers, or \'let me know\'. Your response IS the complete deliverable.\n';
        return prompt;
    }

    // ─── Fix Generation Prompt ──────────────────────────────────────────

    _buildFixPrompt(message, analysisContent, projectFiles, workspaceCtx) {
        let prompt = '# SYSTEM: Vigthoria Agent — Code Fix Generator\n\n';
        prompt += 'You are generating EXACT file fixes that will be automatically applied to the user\'s project.\n';
        prompt += 'The system will parse your response for ```fix blocks and overwrite files with your content.\n\n';
        prompt += '## Previous Analysis\n' + analysisContent.substring(0, 20000) + '\n\n';

        prompt += '## Files Available For Modification\n';
        for (const f of projectFiles) {
            prompt += '\n### ' + f.path + ' (' + f.size + ' bytes' + (f.damaged ? ', ⚠️ PREVIOUSLY DAMAGED — DO NOT modify this file' : '') + ')\n';
            prompt += '```\n' + f.content + '\n```\n';
        }

        prompt += '\n## OUTPUT FORMAT (MANDATORY — the system parses this)\n\n';
        prompt += 'For EACH file that needs changes, output EXACTLY this format:\n\n';
        prompt += '```fix\nFILE: relative/path/to/file.ext\n---CONTENT---\n';
        prompt += '// Your complete corrected file content here\n';
        prompt += '// Include ALL original code plus your fixes\n';
        prompt += '---END---\n```\n\n';

        prompt += '## RULES (MANDATORY — VIOLATION WILL CORRUPT USER FILES)\n';
        prompt += '1. Output the COMPLETE file content for each file — EVERY SINGLE LINE from the original must appear in your output. The system will OVERWRITE the entire file with your content.\n';
        prompt += '2. Preserve ALL existing working functionality — every function, every handler, every export.\n';
        prompt += '3. For .js, .ts, .py files ONLY: add a brief comment (// FIXED: ...) next to lines you changed. NEVER add // comments to .json, .yaml, .yml, .toml, or .xml files.\n';
        prompt += '4. NEVER use placeholder comments like \'// ... rest of the file ...\' or \'// remaining code unchanged\' or \'// existing code continues\'. These are FORBIDDEN — they will destroy the file.\n';
        prompt += '5. If a file is too long to include completely in your output, DO NOT include it at all. Skip it entirely. A partial file is worse than no fix.\n';
        prompt += '6. Your output for each file MUST contain at least as many lines as the original. If your version is shorter, you have lost code.\n';
        prompt += '7. Every module.exports, export default, or export statement from the original MUST appear in your output.\n';
        prompt += '8. Priority: security > crashes > bugs > missing features > optimization\n';
        prompt += '9. Only include files that actually need changes\n';
        prompt += '10. Use the EXACT file paths shown above (case-sensitive)\n';
        prompt += '11. Do NOT add explanatory text outside the ```fix blocks — the system only reads the blocks\n';
        prompt += '12. Do NOT end with questions or offers — output the fix blocks and stop\n\n';
        prompt += 'Generate the ```fix blocks now:\n';
        return prompt;
    }
}

module.exports = { AgentMode };
