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

/**
 * OPERATOR Mode — DevOps & Infrastructure AI Assistant (Multi-Phase)
 * 
 * Flow:
 * 0. Workspace & Infra Discovery — scan files, detect DevOps configs
 * 1. AI Operation Plan — generate dynamic task breakdown
 * 2. Deep File Reading — read up to 40 files (60KB each), DOCX support, DevOps priority
 * 3. Deep AI Analysis — full infrastructure audit with real file contents
 * 4. Fix Generation — optional auto-fix with truncation guards
 */
class OperatorMode {
    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('operator', stepIndex, status);
        }
    }

    sendStream(content) {
        if (this.webviewProvider && this.webviewProvider._view) {
            try {
                this.webviewProvider._view.webview.postMessage({
                    type: 'streamUpdate',
                    data: { content }
                });
            } 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 */ }
        }
    }

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

    // ─── Operation type detection ───────────────────────────────────────

    detectOperationType(message) {
        const lower = message.toLowerCase();
        if (/\b(deploy|release|publish|ship|launch|production|staging|rollout)\b/.test(lower)) return 'deployment';
        if (/\b(config|configure|setup|settings|environment|env|variables|\.env)\b/.test(lower)) return 'configuration';
        if (/\b(monitor|logs?|metrics|health|status|performance|alerts?|watch)\b/.test(lower)) return 'monitoring';
        if (/\b(security|secure|ssl|tls|certificate|auth|permission|access|firewall)\b/.test(lower)) return 'security';
        if (/\b(database|db|sql|migrate|migration|backup|restore|postgres|mysql|mongo)\b/.test(lower)) return 'database';
        if (/\b(infrastructure|infra|server|docker|kubernetes|k8s|container|nginx|apache|systemd|pm2)\b/.test(lower)) return 'infrastructure';
        if (/\b(automate|automation|script|cron|schedule|pipeline|ci|cd|workflow|github\s*actions)\b/.test(lower)) return 'automation';
        return 'general';
    }

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

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

        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';
            }

            const devopsPatterns = [
                'Dockerfile', 'docker-compose.yml', 'docker-compose.yaml',
                '.dockerignore', 'Caddyfile', 'nginx.conf',
                'ecosystem.config.js', 'ecosystem.config.cjs',
                '.github', '.gitlab-ci.yml', 'Jenkinsfile',
                'Makefile', 'Procfile', 'serverless.yml',
                '.env', '.env.example',
                'k8s', 'terraform'
            ];
            for (const p of devopsPatterns) {
                if (entries.includes(p)) ctx.devopsFiles.push(p);
            }
        } 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 Operation Planning ───────────────────────────────────

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

RULES:
- Return ONLY a valid JSON array of short task descriptions (strings)
- Each task should be 3-8 words, action-oriented, DevOps focused
- Simple operations: 2-3 tasks
- Medium operations: 4-6 tasks
- Complex infrastructure work: 6-10 tasks
- Tasks should be specific to the operation requested

PROJECT: ${workspaceCtx ? workspaceCtx.name + ' (' + workspaceCtx.projectType + ')' : 'Unknown'}
DEVOPS FILES: ${workspaceCtx && workspaceCtx.devopsFiles.length > 0 ? workspaceCtx.devopsFiles.join(', ') : 'none detected'}

USER REQUEST: "${message}"

Return ONLY the JSON array. Example: ["Check current service health","Review Docker configuration","Analyze deployment pipeline","Plan scaling strategy","Generate deployment guide"]`;

        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('Operator plan parsing failed:', err.message);
        }

        // Smart fallback plan — detect intent locally
        const fixWords = /\b(fix|create|setup|configure|deploy|generate|implement|add|write|update|migrate|install|upgrade|provision)\b/i;
        if (fixWords.test(message)) {
            return ['Scanning infrastructure', 'Reading config files', 'Analyzing architecture', 'Identifying issues', 'Planning changes', 'Generating fixes', 'Applying modifications', 'Verifying results'];
        }
        return ['Scanning infrastructure', 'Reading config files', 'Analyzing architecture', 'Checking security', 'Reviewing deployments', 'Generating report'];
    }

    // ─── Deep File Reader (DevOps focused, async) ───────────────────────

    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', '.json', '.yaml', '.yml', '.toml', '.md', '.sql', '.sh', '.bat', '.ps1', '.env', '.cfg', '.ini', '.xml', '.conf', '.html', '.css', '.docx']);
        const devopsPriority = [
            /^(Dockerfile|docker-compose|Caddyfile|nginx\.conf|Makefile|Procfile|Vagrantfile)$/i,
            /^(ecosystem\.config|pm2\.config|serverless)\./i,
            /^(package|tsconfig)\./i,
            /^\.env/i,
            /deploy|pipeline|ci|cd|Jenkinsfile|\.github/i,
            /(server|main|index|app|config|setup)\./i,
            /(security|auth|middleware|proxy)/i,
            /k8s|kubernetes|terraform|ansible|helm/i,
            /(schema|migration|model|route|controller|handler|service|database|db)\./i,
            /\.(sql|prisma|graphql|gql)$/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') && entry.name !== '.github') 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') || !ext) {
                            const relPath = path.relative(workspacePath, fullPath);
                            const isPriority = devopsPriority.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);

        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 {
                if (i > 0 && i % 3 === 0) {
                    await new Promise(r => setTimeout(r, 0));
                }

                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');

                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');
                }

                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 Infrastructure Changes ───────────────────────────────────

    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');

                    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;

                        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;
                            }
                        }

                        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('✅ Operator 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('✅ Operator created file:', relPath);
                }
            } catch (err) {
                console.warn('⚠️ Operator could not write ' + relPath + ':', err.message);
            }
        }

        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('⚙️ OPERATOR MODE: Processing message (multi-phase)');

            // ── Phase 0: Workspace & Infra Discovery ──
            this.sendThought('Scanning workspace & infrastructure...', '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');
                    }
                    if (workspaceCtx.devopsFiles.length > 0) {
                        this.sendThought('DevOps files found: ' + workspaceCtx.devopsFiles.join(', '), 'reading');
                    }
                }
            }

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

            const fixWords = /\b(fix|create|setup|configure|deploy|generate|implement|add|write|update|migrate|install|upgrade|provision)\b/i;
            const wantsFix = fixWords.test(message);

            // ── Phase 1: Operation Plan ──
            this.sendThought('Creating operation plan...', 'analyzing');
            const taskPlan = await this.getAIOperationPlan(message, workspaceCtx);
            const steps = taskPlan.map(text => ({ text }));
            if (this.todoProvider) {
                this.todoProvider.setAITaskPlan('operator', steps);
            }
            this.sendProgress('operator', 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('operator', steps, currentStep, steps[currentStep].text);
                    } else if (status === 'completed') {
                        this.sendProgress('operator', steps, currentStep + 1, steps[currentStep].text);
                        currentStep++;
                    }
                }
            };

            // ── Phase 2: Deep File Reading ──
            markStep('in-progress');
            let projectFiles = [];
            if (workspacePath) {
                this.sendThought('Reading infrastructure & config files...', '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 + ' files (' + totalKB + ' KB)', 'reading');
            }
            markStep('completed');

            // ── Phase 3: Deep AI Analysis ──
            markStep('in-progress');
            this.sendThought('Running deep infrastructure analysis...', 'thinking');

            const analysisPrompt = this._buildDeepOperatorPrompt(message, workspaceCtx, projectFiles, activeFile, wantsFix);

            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: 'operator',
                    skipContext: true,
                    rawMessage: message,
                    operatorPrompt: analysisPrompt,
                    operatorOperationType: this.detectOperationType(message),
                    workspace: workspaceCtx ? {
                        name: workspaceCtx.name,
                        path: workspaceCtx.path,
                        projectType: workspaceCtx.projectType,
                        fileTree: workspaceCtx.fileTree,
                        devopsFiles: workspaceCtx.devopsFiles
                    } : null,
                    activeFile: activeFile || null
                });
            } catch (aiErr) {
                clearInterval(progressTimer);
                console.error('⚙️ OPERATOR AI call failed:', aiErr.message);
                if (this.todoProvider) this.todoProvider.completeAllAITasks('operator');
                return {
                    success: false,
                    content: '## ⚠️ Operator Error\n\nCould not reach Vigthoria AI: ' + aiErr.message + '\n\nPlease check your connection and try again.',
                    actions: [],
                    metadata: { mode: 'operator', error: true, timestamp: new Date().toISOString() }
                };
            }
            clearInterval(progressTimer);

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

            if (analysisContent.includes('Could not reach Vigthoria AI') || analysisContent.includes('fetch failed') || (analysisResponse.metadata && analysisResponse.metadata.offline)) {
                console.error('⚙️ OPERATOR: AI returned error response instead of analysis');
                if (this.todoProvider) this.todoProvider.completeAllAITasks('operator');
                return {
                    success: false,
                    content: '## ⚠️ Operator 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: 'operator', error: true, timestamp: new Date().toISOString() }
                };
            }

            while (currentStep < analysisPhaseEnd && currentStep < steps.length) {
                markStep('completed');
            }

            // ── Phase 4: Implementation (if requested) ──
            let fixContent = '';
            let appliedFixes = [];
            if (wantsFix) {
                if (currentStep < steps.length) markStep('in-progress');
                this.sendThought('Generating infrastructure changes...', 'generating');

                let fixPrompt = '# SYSTEM: Vigthoria Operator — Infrastructure Fix Generator\n\n';
                fixPrompt += 'You are generating EXACT config/infrastructure fixes that will be automatically applied.\n';
                fixPrompt += 'The system will parse your response for ```fix blocks and overwrite files with your content.\n\n';
                fixPrompt += '## Previous Analysis\n' + analysisContent.substring(0, 20000) + '\n\n';
                fixPrompt += '## Files Available For Modification\n';
                for (const f of projectFiles) {
                    fixPrompt += '\n### ' + f.path + ' (' + f.size + ' bytes' + (f.damaged ? ', ⚠️ PREVIOUSLY DAMAGED — DO NOT modify this file' : '') + ')\n';
                    fixPrompt += '```\n' + f.content + '\n```\n';
                }
                fixPrompt += '\n## OUTPUT FORMAT (MANDATORY)\n\n';
                fixPrompt += 'For EACH file that needs changes, output EXACTLY:\n\n';
                fixPrompt += '```fix\nFILE: relative/path/to/file.ext\n---CONTENT---\n';
                fixPrompt += '// Complete corrected file content with // FIXED: comments\n';
                fixPrompt += '---END---\n```\n\n';
                fixPrompt += 'Rules (MANDATORY — VIOLATION WILL CORRUPT USER FILES):\n';
                fixPrompt += '1. Output the COMPLETE file content — EVERY SINGLE LINE. The system will OVERWRITE the entire file.\n';
                fixPrompt += '2. Preserve ALL existing working functionality — every function, handler, export.\n';
                fixPrompt += '3. NEVER use placeholder comments like \'// ... rest of the file ...\' or \'// remaining code unchanged\'. These are FORBIDDEN.\n';
                fixPrompt += '4. If a file is too long to output completely, DO NOT include it. Skip it entirely.\n';
                fixPrompt += '5. For .json, .yaml, .yml files: NEVER add // comments — they break the format.\n';
                fixPrompt += '6. Every module.exports/export from the original MUST appear in your output.\n';
                fixPrompt += '7. Use EXACT file paths from above\n';
                fixPrompt += '8. Do NOT add text outside ```fix blocks\n';
                fixPrompt += '9. Do NOT end with questions or offers\n\n';
                fixPrompt += 'Generate the ```fix blocks now:\n';

                try {
                    const fixResponse = await this.aiService.processMessage(fixPrompt, {
                        mode: 'operator',
                        skipContext: true
                    });
                    fixContent = fixResponse.content || fixResponse.response || '';

                    if (workspacePath && fixContent) {
                        this.sendThought('Applying infrastructure 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 + ' infrastructure 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('⚠️ Operator fix generation failed:', fixErr.message);
                    fixContent = '\n\n> ⚠️ Fix generation 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('operator', steps, steps.length, 'All tasks completed');
            } else {
                this.sendProgress('operator', steps, currentStep, 'Completed with issues');
                this.sendThought('⚠️ Some tasks could not be completed', 'warning');
            }

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

            const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
            this.sendThought('Operation 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 Infrastructure Changes\n\n' + fixContent;
                finalContent += '\n\n### Files Modified:\n' + appliedFixes.map(f => '- ✅ ' + f).join('\n');
            } else if (fixContent) {
                finalContent += '\n\n---\n\n## ⚙️ Suggested Changes\n\n' + fixContent;
            }

            finalContent += '\n\n---\n*Operator analyzed ' + projectFiles.length + ' files in ' + elapsed + 's*';

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

        } catch (error) {
            console.error('❌ OPERATOR MODE Error:', error);
            if (this.todoProvider) this.todoProvider.completeAllAITasks('operator');
            return {
                success: false,
                content: '## ❌ Operator Error\n\n' + (error.message || 'Unknown error') + '. Please try again.',
                actions: [],
                metadata: { mode: 'operator', 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 Operator Prompt (with real file contents) ─────────────────

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

        let prompt = '# SYSTEM: Vigthoria Operator — Autonomous DevOps & Infrastructure Engine\n\n';
        prompt += 'You are analyzing REAL project files provided below. You are NOT a chatbot.\n';
        prompt += 'You are an autonomous DevOps agent that reads real configs and produces real fixes.\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\n';
            prompt += '- Say \'not visible in the provided portion\' instead of \'does not exist\'\n';
            prompt += '- For large files, note what sections you CAN see vs what might be hidden\n\n';
        }

        if (docFiles.length > 0) {
            prompt += '## 📄 DOCUMENT FILES INCLUDED\n';
            prompt += 'The following document files have been converted to plain text for analysis:\n';
            for (const f of docFiles) {
                prompt += '- **' + f.path + '** (extracted from ' + path.extname(f.name).toUpperCase().replace('.', '') + ' document)\n';
            }
            prompt += 'Compare document content against the actual codebase when relevant.\n\n';
        }

        prompt += '## CRITICAL RULES (violations will cause system failure)\n';
        prompt += '- ONLY reference files, configs, and services that EXIST in the source code below\n';
        prompt += '- NEVER fabricate file names, paths, config keys, or service names\n';
        prompt += '- NEVER give generic advice — every recommendation must cite the EXACT file and line\n';
        prompt += '- NEVER use placeholder examples like \'example.com\' or \'your-project\'\n';
        prompt += '- NEVER end your response with a question, \'let me know\', or \'would you like\'\n';
        prompt += '- Your response IS the deliverable — it must be complete and actionable\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.devopsFiles.length > 0) {
                prompt += '- **DevOps Files:** ' + workspaceCtx.devopsFiles.join(', ') + '\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## Source Files (' + projectFiles.length + ' files)\n';
            for (const file of projectFiles) {
                const ext = file.isDocument ? 'text' : (path.extname(file.name).replace('.', '') || 'text');
                let label = file.path;
                if (file.truncated && file.splitRead) label += ' (⚠️ TRUNCATED — first+last portions only, ' + file.size + ' bytes total)';
                else if (file.truncated) label += ' (⚠️ TRUNCATED, ' + file.size + ' bytes total)';
                if (file.isDocument) label += ' (extracted text from document)';
                if (file.damaged) label += ' (⚠️ PREVIOUSLY DAMAGED — DO NOT include in fix blocks)';
                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## MANDATORY RESPONSE FORMAT\n\n';
        prompt += '### 1. Infrastructure Summary\n';
        prompt += 'List what exists: servers, databases, configs, CI/CD pipelines, containers — from the REAL files.\n\n';
        prompt += '### 2. Critical Issues (security, crashes, data loss)\n';
        prompt += 'Each issue must cite: **File:** `path`, **Problem:** exact description, **Impact:** severity\n\n';
        prompt += '### 3. Configuration Improvements\n';
        prompt += 'Improvements to existing configs with exact before/after snippets from real files.\n\n';
        prompt += '### 4. Missing Infrastructure\n';
        prompt += 'What\'s missing based on project type (logging, monitoring, backups, health checks, etc.)\n\n';

        if (wantsFix) {
            prompt += '### 5. APPLIED CHANGES (MANDATORY for fix requests)\n';
            prompt += 'For EACH file that needs changes, output the COMPLETE file:\n\n';
            prompt += '```fix\nFILE: relative/path/to/file.ext\n---CONTENT---\n';
            prompt += '// Complete corrected file content — EVERY LINE from the original plus your changes\n';
            prompt += '---END---\n```\n\n';
            prompt += 'CRITICAL: The system OVERWRITES the entire file. NEVER use placeholder comments like \'// ... rest of file ...\'. If a file is too long to output completely, skip it. For .json/.yaml files, NEVER add // comments.\n\n';
        }

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

module.exports = { OperatorMode };
