import * as fse from "fs-extra";
import { readdir, mkdtemp } from "node:fs/promises";
import * as path from "node:path";
import * as os from "node:os";
import { BackupError, RollbackError } from "../errors/index.js";
import { generateBackupDirname } from "../utils/file-operations.js";
/**
 * Service for backing up and restoring files during operations
 */
export class BackupService {
    backupDir = null;
    destDir = "";
    persistentBackupDirname = null;
    logger;
    /**
     * Initialize backup service with logger
     * @param logger - Logger instance for backup operations
     */
    constructor(logger) {
        this.logger = logger;
    }
    async init(destDir) {
        this.destDir = destDir;
        this.backupDir = await mkdtemp(path.join(os.tmpdir(), "lisa-backup-"));
        this.logger.info(`Backup directory: ${this.backupDir}`);
    }
    async backup(absolutePath) {
        if (!this.backupDir) {
            throw new BackupError("backup", "Backup service not initialized");
        }
        try {
            if (await fse.pathExists(absolutePath)) {
                const relativePath = path.relative(this.destDir, absolutePath);
                const backupPath = path.join(this.backupDir, relativePath);
                await fse.ensureDir(path.dirname(backupPath));
                await fse.copy(absolutePath, backupPath);
            }
        }
        catch (error) {
            throw new BackupError("backup", `Failed to backup ${absolutePath}: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async persistentBackup(absolutePath) {
        try {
            if (await fse.pathExists(absolutePath)) {
                // Generate backup directory name on first call
                if (!this.persistentBackupDirname) {
                    this.persistentBackupDirname = generateBackupDirname();
                }
                // Get relative path from destination directory
                const relativePath = path.relative(this.destDir, absolutePath);
                // Create backup in .lisabak/{timestamp}/{relative-path}
                const backupBaseDir = path.join(this.destDir, ".lisabak", this.persistentBackupDirname);
                const backupPath = path.join(backupBaseDir, relativePath);
                await fse.ensureDir(path.dirname(backupPath));
                await fse.copy(absolutePath, backupPath);
                this.logger.info(`Backup created: .lisabak/${this.persistentBackupDirname}/${relativePath}`);
            }
        }
        catch (error) {
            throw new BackupError("backup", `Failed to create persistent backup of ${absolutePath}: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async rollback() {
        if (!this.backupDir || !(await fse.pathExists(this.backupDir))) {
            throw new RollbackError("No backup directory available");
        }
        this.logger.warn("Rolling back changes...");
        try {
            // Find all backed up files and restore them
            const files = await this.listFilesRecursive(this.backupDir);
            for (const backupFile of files) {
                const relativePath = path.relative(this.backupDir, backupFile);
                const destFile = path.join(this.destDir, relativePath);
                await fse.copy(backupFile, destFile);
                this.logger.info(`Restored: ${relativePath}`);
            }
            await this.cleanup();
            this.logger.success("Rollback complete");
        }
        catch (error) {
            throw new RollbackError(`Failed to rollback: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async cleanup() {
        if (this.backupDir && (await fse.pathExists(this.backupDir))) {
            await fse.remove(this.backupDir);
            this.backupDir = null;
        }
    }
    /**
     * Recursively list all files in a directory
     * @param dir - Directory to scan recursively
     * @returns Array of absolute file paths
     */
    async listFilesRecursive(dir) {
        const files = [];
        const entries = await readdir(dir, { withFileTypes: true });
        for (const entry of entries) {
            const fullPath = path.join(dir, entry.name);
            if (entry.isDirectory()) {
                const subFiles = await this.listFilesRecursive(fullPath);
                files.push(...subFiles);
            }
            else if (entry.isFile()) {
                files.push(fullPath);
            }
        }
        return files;
    }
}
/**
 * No-op backup service for dry-run mode
 */
export class DryRunBackupService {
    async init(_destDir) {
        // No-op
    }
    async backup(_absolutePath) {
        // No-op
    }
    async persistentBackup(_absolutePath) {
        // No-op
    }
    async rollback() {
        // No-op
    }
    async cleanup() {
        // No-op
    }
}
//# sourceMappingURL=backup.js.map