import * as fs from 'fs';
import * as path from 'path';
import { FileSystemError, TransactionError } from './errors';
import { logInfo, logError } from '../utils';

interface BackupEntry {
    path: string;
    type: 'modified' | 'created' | 'deleted';
    originalContent?: string;
}

export class TransactionManager {
    private backups: Map<string, BackupEntry> = new Map();
    private externalVerifications: Map<string, boolean> = new Map();
    private isTransactionActive: boolean = false;

    constructor() {}

    /**
     * Starts a new transaction.
     */
    public begin() {
        if (this.isTransactionActive) {
            throw new TransactionError('A transaction is already in progress.');
        }
        this.backups.clear();
        this.externalVerifications.clear();
        this.isTransactionActive = true;
        logInfo('Transaction started.');
    }

    /**
     * Records the success of an external action (e.g., API call).
     */
    public recordExternalAction(actionId: string, success: boolean) {
        if (!this.isTransactionActive) return;
        this.externalVerifications.set(actionId, success);
        logInfo(`External action recorded: ${actionId} (success: ${success})`);
    }

    /**
     * Checks if all recorded external actions were successful.
     */
    public isFullyVerified(): boolean {
        for (const success of this.externalVerifications.values()) {
            if (!success) return false;
        }
        return true;
    }

    /**
     * Records a file state before modification.
     */
    public async record(filePath: string) {
        if (!this.isTransactionActive) return;
        if (this.backups.has(filePath)) return; // Already backed up in this transaction

        try {
            if (fs.existsSync(filePath)) {
                const content = fs.readFileSync(filePath, 'utf-8');
                this.backups.set(filePath, {
                    path: filePath,
                    type: 'modified',
                    originalContent: content
                });
            } else {
                this.backups.set(filePath, {
                    path: filePath,
                    type: 'created'
                });
            }
        } catch (error: any) {
            throw new FileSystemError(`Failed to backup file: ${error.message}`, filePath, error);
        }
    }

    /**
     * Commits the transaction, clearing all backups.
     */
    public commit() {
        if (!this.isTransactionActive) return;
        this.backups.clear();
        this.isTransactionActive = false;
        logInfo('Transaction committed successfully.');
    }

    /**
     * Rolls back all changes made during the transaction.
     */
    public rollback() {
        if (!this.isTransactionActive) return;
        
        logInfo(`Rolling back ${this.backups.size} changes...`);
        
        for (const entry of this.backups.values()) {
            try {
                if (entry.type === 'created') {
                    if (fs.existsSync(entry.path)) {
                        fs.unlinkSync(entry.path);
                        logInfo(`Rollback: Deleted created file ${entry.path}`);
                    }
                } else if (entry.type === 'modified' || entry.type === 'deleted') {
                    if (entry.originalContent !== undefined) {
                        const dir = path.dirname(entry.path);
                        if (!fs.existsSync(dir)) {
                            fs.mkdirSync(dir, { recursive: true });
                        }
                        fs.writeFileSync(entry.path, entry.originalContent, 'utf-8');
                        logInfo(`Rollback: Restored file ${entry.path}`);
                    }
                }
            } catch (error: any) {
                logError(`Failed to rollback change for ${entry.path}`, error);
            }
        }

        this.backups.clear();
        this.isTransactionActive = false;
        logInfo('Transaction rollback completed.');
    }

    public isActive(): boolean {
        return this.isTransactionActive;
    }

    /** Snapshot of file paths currently recorded in the active transaction. */
    public getRecordedFiles(): { path: string; type: 'modified' | 'created' | 'deleted' }[] {
        return Array.from(this.backups.values()).map(b => ({ path: b.path, type: b.type }));
    }
}

// Export a singleton instance if needed, or instantiate per AgentExecutor
