/**
 * Vigthoria Inline Diff Provider — Accept/Reject AI Changes
 * 
 * Like Cursor's "Apply" — shows green/red diffs inline in the editor.
 * User can Accept (Tab), Reject (Escape), or Accept All.
 * 
 * Architecture:
 * - Uses VS Code DecorationTypes for green (add) / red (delete) highlights
 * - Manages pending changes per-file
 * - Integrates with Agent mode to apply AI-suggested edits
 * - Full undo support via WorkspaceEdit
 */

const vscode = require('vscode');
const path = require('path');

// Decoration types for diff visualization
const addedDecorationType = vscode.window.createTextEditorDecorationType({
    backgroundColor: 'rgba(40, 167, 69, 0.15)',
    border: '1px solid rgba(40, 167, 69, 0.3)',
    isWholeLine: true,
    before: {
        contentText: '+',
        color: 'rgba(40, 167, 69, 0.7)',
        margin: '0 8px 0 0'
    }
});

const removedDecorationType = vscode.window.createTextEditorDecorationType({
    backgroundColor: 'rgba(220, 53, 69, 0.15)',
    border: '1px solid rgba(220, 53, 69, 0.3)',
    isWholeLine: true,
    textDecoration: 'line-through',
    before: {
        contentText: '-',
        color: 'rgba(220, 53, 69, 0.7)',
        margin: '0 8px 0 0'
    }
});

const pendingDecorationType = vscode.window.createTextEditorDecorationType({
    after: {
        contentText: '  [Tab: Accept | Esc: Reject]',
        color: 'rgba(150, 150, 150, 0.6)',
        fontStyle: 'italic',
        fontSize: '11px'
    }
});

class VigthoriaDiffProvider {
    constructor() {
        // Map of file URI string -> pending changes
        this.pendingChanges = new Map();
        this.activeDiffEditor = null;
        this.statusBarItem = null;
        this.changeCounter = 0;
        
        console.log('[DIFF-PROVIDER] ✨ Initialized');
    }

    register(context) {
        // Commands
        context.subscriptions.push(
            vscode.commands.registerCommand('vigthoria.acceptChange', () => this.acceptCurrentChange()),
            vscode.commands.registerCommand('vigthoria.rejectChange', () => this.rejectCurrentChange()),
            vscode.commands.registerCommand('vigthoria.acceptAllChanges', () => this.acceptAllChanges()),
            vscode.commands.registerCommand('vigthoria.rejectAllChanges', () => this.rejectAllChanges()),
            vscode.commands.registerCommand('vigthoria.showDiff', (original, modified, title) => 
                this.showSideBySideDiff(original, modified, title)
            ),
            vscode.commands.registerCommand('vigthoria.applyFileChanges', (changes) =>
                this.applyFileChanges(changes)
            )
        );

        // Keybindings — Tab accepts, Esc rejects when diff is active
        context.subscriptions.push(
            vscode.commands.registerCommand('vigthoria.diffTab', () => {
                if (this.hasPendingChanges()) {
                    this.acceptCurrentChange();
                } else {
                    // Pass through to default Tab behavior
                    vscode.commands.executeCommand('tab');
                }
            }),
            vscode.commands.registerCommand('vigthoria.diffEscape', () => {
                if (this.hasPendingChanges()) {
                    this.rejectCurrentChange();
                } else {
                    vscode.commands.executeCommand('cancelSelection');
                }
            })
        );

        // Status bar for pending changes
        this.statusBarItem = vscode.window.createStatusBarItem(
            vscode.StatusBarAlignment.Right, 98
        );
        this.statusBarItem.command = 'vigthoria.acceptAllChanges';
        context.subscriptions.push(this.statusBarItem);

        // Watch for editor changes to clear decorations
        context.subscriptions.push(
            vscode.window.onDidChangeActiveTextEditor((editor) => {
                if (editor) {
                    this._refreshDecorations(editor);
                }
            })
        );

        console.log('[DIFF-PROVIDER] ✅ Registered');
    }

    /**
     * Apply changes to a file with inline diff visualization
     * This is the main entry point for AI-generated changes
     * 
     * @param {Object} fileChange - { filePath, originalContent, newContent, description }
     */
    async proposeChanges(fileChange) {
        const { filePath, originalContent, newContent, description } = fileChange;
        
        const uri = vscode.Uri.file(filePath);
        
        try {
            // Open the file
            const document = await vscode.workspace.openTextDocument(uri);
            const editor = await vscode.window.showTextDocument(document);
            
            // Compute the diff
            const changes = this._computeLineDiff(
                originalContent || document.getText(),
                newContent
            );
            
            if (changes.length === 0) {
                vscode.window.showInformationMessage('No changes detected');
                return;
            }

            // Store pending changes
            const changeSet = {
                id: ++this.changeCounter,
                filePath,
                uri: uri.toString(),
                changes,
                description: description || 'AI-suggested changes',
                originalContent: originalContent || document.getText(),
                newContent,
                timestamp: Date.now()
            };
            
            this.pendingChanges.set(uri.toString(), changeSet);
            
            // Apply visual decorations
            this._applyDecorations(editor, changes);
            
            // Update status bar
            this._updateStatusBar();
            
            // Show notification
            const action = await vscode.window.showInformationMessage(
                `${changes.length} changes proposed: ${description}`,
                'Accept All', 'Review', 'Reject All'
            );
            
            if (action === 'Accept All') {
                await this.acceptAllChangesForFile(uri.toString());
            } else if (action === 'Reject All') {
                this.rejectAllChangesForFile(uri.toString());
            }
            // 'Review' — leave decorations visible for manual accept/reject
            
        } catch (error) {
            console.error('[DIFF-PROVIDER] Error proposing changes:', error);
            vscode.window.showErrorMessage(`Failed to propose changes: ${error.message}`);
        }
    }

    /**
     * Apply a batch of file changes from an AI agent response
     * 
     * @param {Array} changes - Array of { filePath, action, content, description }
     *   action: 'create' | 'modify' | 'delete'
     */
    async applyFileChanges(changes) {
        if (!changes || !Array.isArray(changes) || changes.length === 0) {
            return { success: false, message: 'No changes to apply' };
        }

        const results = [];

        for (const change of changes) {
            try {
                switch (change.action) {
                    case 'create': {
                        const uri = vscode.Uri.file(change.filePath);
                        const dir = path.dirname(change.filePath);
                        
                        // Create directory if needed
                        await vscode.workspace.fs.createDirectory(vscode.Uri.file(dir));
                        
                        // Write file
                        const content = Buffer.from(change.content || '', 'utf-8');
                        await vscode.workspace.fs.writeFile(uri, content);
                        
                        results.push({ filePath: change.filePath, action: 'created', success: true });
                        break;
                    }
                    
                    case 'modify': {
                        const uri = vscode.Uri.file(change.filePath);
                        
                        // Read current content
                        let currentContent = '';
                        try {
                            const existing = await vscode.workspace.fs.readFile(uri);
                            currentContent = Buffer.from(existing).toString('utf-8');
                        } catch (e) {
                            // File doesn't exist yet — treat as create
                            const content = Buffer.from(change.content || '', 'utf-8');
                            await vscode.workspace.fs.writeFile(uri, content);
                            results.push({ filePath: change.filePath, action: 'created', success: true });
                            continue;
                        }

                        // If search/replace specified
                        if (change.search && change.replace !== undefined) {
                            const newContent = currentContent.replace(change.search, change.replace);
                            await this.proposeChanges({
                                filePath: change.filePath,
                                originalContent: currentContent,
                                newContent: newContent,
                                description: change.description || 'AI modification'
                            });
                        } else if (change.content) {
                            // Full file replacement — show diff
                            await this.proposeChanges({
                                filePath: change.filePath,
                                originalContent: currentContent,
                                newContent: change.content,
                                description: change.description || 'AI modification'
                            });
                        }
                        
                        results.push({ filePath: change.filePath, action: 'proposed', success: true });
                        break;
                    }
                    
                    case 'delete': {
                        const uri = vscode.Uri.file(change.filePath);
                        const confirm = await vscode.window.showWarningMessage(
                            `Delete ${path.basename(change.filePath)}?`,
                            { modal: true },
                            'Delete'
                        );
                        if (confirm === 'Delete') {
                            await vscode.workspace.fs.delete(uri);
                            results.push({ filePath: change.filePath, action: 'deleted', success: true });
                        } else {
                            results.push({ filePath: change.filePath, action: 'skipped', success: false });
                        }
                        break;
                    }
                }
            } catch (error) {
                results.push({ filePath: change.filePath, action: change.action, success: false, error: error.message });
            }
        }

        return { success: true, results };
    }

    /**
     * Show a side-by-side diff in VS Code's native diff editor
     */
    async showSideBySideDiff(originalContent, modifiedContent, title) {
        const originalUri = vscode.Uri.parse(`vigthoria-diff:original/${title || 'Original'}`);
        const modifiedUri = vscode.Uri.parse(`vigthoria-diff:modified/${title || 'Modified'}`);
        
        // Register a temporary content provider
        const provider = new class {
            provideTextDocumentContent(uri) {
                return uri.path.startsWith('original/') ? originalContent : modifiedContent;
            }
        };

        const registration = vscode.workspace.registerTextDocumentContentProvider('vigthoria-diff', provider);
        
        await vscode.commands.executeCommand('vscode.diff',
            originalUri,
            modifiedUri,
            `Vigthoria: ${title || 'AI Changes'}`
        );

        // Clean up after a delay
        setTimeout(() => registration.dispose(), 300000); // 5 min
    }

    /**
     * Accept the current pending change at cursor position
     */
    async acceptCurrentChange() {
        const editor = vscode.window.activeTextEditor;
        if (!editor) return;

        const uriString = editor.document.uri.toString();
        const changeSet = this.pendingChanges.get(uriString);
        if (!changeSet) return;

        // Apply the full new content
        const edit = new vscode.WorkspaceEdit();
        const fullRange = new vscode.Range(
            new vscode.Position(0, 0),
            new vscode.Position(editor.document.lineCount - 1, 
                editor.document.lineAt(editor.document.lineCount - 1).text.length)
        );
        edit.replace(editor.document.uri, fullRange, changeSet.newContent);
        await vscode.workspace.applyEdit(edit);

        // Clear decorations and pending state
        editor.setDecorations(addedDecorationType, []);
        editor.setDecorations(removedDecorationType, []);
        editor.setDecorations(pendingDecorationType, []);
        this.pendingChanges.delete(uriString);
        this._updateStatusBar();

        vscode.window.showInformationMessage('✅ Changes accepted');
    }

    /**
     * Reject the current pending change
     */
    rejectCurrentChange() {
        const editor = vscode.window.activeTextEditor;
        if (!editor) return;

        const uriString = editor.document.uri.toString();
        
        // Clear decorations
        editor.setDecorations(addedDecorationType, []);
        editor.setDecorations(removedDecorationType, []);
        editor.setDecorations(pendingDecorationType, []);
        this.pendingChanges.delete(uriString);
        this._updateStatusBar();

        vscode.window.showInformationMessage('❌ Changes rejected');
    }

    /**
     * Accept all changes across all files
     */
    async acceptAllChanges() {
        const uris = Array.from(this.pendingChanges.keys());
        for (const uri of uris) {
            await this.acceptAllChangesForFile(uri);
        }
    }

    /**
     * Accept all changes for a specific file
     */
    async acceptAllChangesForFile(uriString) {
        const changeSet = this.pendingChanges.get(uriString);
        if (!changeSet) return;

        try {
            const uri = vscode.Uri.parse(uriString);
            const document = await vscode.workspace.openTextDocument(uri);
            const edit = new vscode.WorkspaceEdit();
            const fullRange = new vscode.Range(
                new vscode.Position(0, 0),
                new vscode.Position(document.lineCount - 1,
                    document.lineAt(document.lineCount - 1).text.length)
            );
            edit.replace(uri, fullRange, changeSet.newContent);
            await vscode.workspace.applyEdit(edit);
            await document.save();
        } catch (error) {
            console.error('[DIFF-PROVIDER] Accept error:', error);
        }

        this.pendingChanges.delete(uriString);

        // Clear decorations if the file is visible
        const editor = vscode.window.activeTextEditor;
        if (editor && editor.document.uri.toString() === uriString) {
            editor.setDecorations(addedDecorationType, []);
            editor.setDecorations(removedDecorationType, []);
            editor.setDecorations(pendingDecorationType, []);
        }
        this._updateStatusBar();
    }

    /**
     * Reject all changes
     */
    rejectAllChanges() {
        for (const [uriString] of this.pendingChanges) {
            this.rejectAllChangesForFile(uriString);
        }
    }

    rejectAllChangesForFile(uriString) {
        this.pendingChanges.delete(uriString);
        
        const editor = vscode.window.activeTextEditor;
        if (editor && editor.document.uri.toString() === uriString) {
            editor.setDecorations(addedDecorationType, []);
            editor.setDecorations(removedDecorationType, []);
            editor.setDecorations(pendingDecorationType, []);
        }
        this._updateStatusBar();
    }

    hasPendingChanges() {
        return this.pendingChanges.size > 0;
    }

    /**
     * Compute line-level diff between two texts
     */
    _computeLineDiff(original, modified) {
        const origLines = original.split('\n');
        const modLines = modified.split('\n');
        const changes = [];
        
        // Simple LCS-based diff
        const lcs = this._lcs(origLines, modLines);
        let oi = 0, mi = 0, li = 0;
        
        while (oi < origLines.length || mi < modLines.length) {
            if (li < lcs.length && oi < origLines.length && origLines[oi] === lcs[li]) {
                if (mi < modLines.length && modLines[mi] === lcs[li]) {
                    // Unchanged line
                    oi++; mi++; li++;
                } else {
                    // Line added in modified
                    changes.push({ type: 'add', line: mi, content: modLines[mi] });
                    mi++;
                }
            } else if (oi < origLines.length && (li >= lcs.length || origLines[oi] !== lcs[li])) {
                // Line removed from original
                changes.push({ type: 'remove', line: oi, content: origLines[oi] });
                oi++;
            } else if (mi < modLines.length) {
                changes.push({ type: 'add', line: mi, content: modLines[mi] });
                mi++;
            } else {
                oi++; mi++;
            }
        }
        
        return changes;
    }

    /**
     * Longest Common Subsequence (for diff computation)
     */
    _lcs(a, b) {
        const m = a.length, n = b.length;
        // Use space-optimized approach for large files
        if (m > 5000 || n > 5000) {
            return this._lcsSimplified(a, b);
        }
        
        const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
        
        for (let i = 1; i <= m; i++) {
            for (let j = 1; j <= n; j++) {
                if (a[i - 1] === b[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        
        // Backtrack to find the LCS
        const result = [];
        let i = m, j = n;
        while (i > 0 && j > 0) {
            if (a[i - 1] === b[j - 1]) {
                result.unshift(a[i - 1]);
                i--; j--;
            } else if (dp[i - 1][j] > dp[i][j - 1]) {
                i--;
            } else {
                j--;
            }
        }
        return result;
    }

    /**
     * Simplified LCS for very large files (chunk-based)
     */
    _lcsSimplified(a, b) {
        // For very large files, use a simpler line-matching approach
        const bSet = new Set(b);
        return a.filter(line => bSet.has(line));
    }

    /**
     * Apply visual decorations to show diff
     */
    _applyDecorations(editor, changes) {
        const addDecorations = [];
        const removeDecorations = [];
        const pendingDecorations = [];

        for (const change of changes) {
            const line = Math.min(change.line, editor.document.lineCount - 1);
            const range = new vscode.Range(
                new vscode.Position(line, 0),
                new vscode.Position(line, editor.document.lineAt(line).text.length)
            );

            if (change.type === 'add') {
                addDecorations.push({ range });
            } else if (change.type === 'remove') {
                removeDecorations.push({ range });
            }
        }

        // Add pending indicator on the first change line
        if (changes.length > 0) {
            const firstLine = Math.min(changes[0].line, editor.document.lineCount - 1);
            pendingDecorations.push({
                range: new vscode.Range(
                    new vscode.Position(firstLine, 0),
                    new vscode.Position(firstLine, 0)
                )
            });
        }

        editor.setDecorations(addedDecorationType, addDecorations);
        editor.setDecorations(removedDecorationType, removeDecorations);
        editor.setDecorations(pendingDecorationType, pendingDecorations);
    }

    _refreshDecorations(editor) {
        const uriString = editor.document.uri.toString();
        const changeSet = this.pendingChanges.get(uriString);
        if (changeSet) {
            this._applyDecorations(editor, changeSet.changes);
        }
    }

    _updateStatusBar() {
        if (!this.statusBarItem) return;
        
        const count = this.pendingChanges.size;
        if (count > 0) {
            this.statusBarItem.text = `$(git-compare) ${count} pending change${count > 1 ? 's' : ''}`;
            this.statusBarItem.tooltip = 'Click to accept all pending changes';
            this.statusBarItem.show();
        } else {
            this.statusBarItem.hide();
        }
    }

    dispose() {
        if (this.statusBarItem) this.statusBarItem.dispose();
        this.pendingChanges.clear();
    }
}

module.exports = { VigthoriaDiffProvider };
