"use strict";
/**
 * Commit Manager
 * Orchestrates the commit workflow - combines diff generation and storage
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommitManager = void 0;
const crypto = __importStar(require("crypto"));
const child_process_1 = require("child_process");
const yaml = __importStar(require("js-yaml"));
const commit_storage_1 = require("./commit-storage");
const diff_engine_1 = require("./diff-engine");
const workflow_parser_1 = require("../parser/workflow-parser");
class CommitManager {
    storage;
    diffEngine;
    constructor(projectRoot) {
        this.storage = new commit_storage_1.CommitStorage(projectRoot);
        this.diffEngine = new diff_engine_1.DiffEngine();
    }
    /**
     * Initialize the commit system
     */
    initialize() {
        this.storage.initialize();
    }
    /**
     * Check if .bangstack directory exists
     */
    isInitialized() {
        return this.storage.exists();
    }
    /**
     * Create a new commit
     */
    async createCommit(options) {
        // Ensure .bangstack exists
        if (!this.isInitialized()) {
            throw new Error('Bangstack not initialized. Run "bangstack init" first.');
        }
        // Load current config
        const currentConfig = yaml.load(this.storage.loadConfig());
        // Get previous config (from options or HEAD)
        let previousConfig = options.previousConfig;
        if (!previousConfig) {
            const head = this.storage.getHead();
            if (head) {
                // Reconstruct previous state from commits
                previousConfig = await this.reconstructConfig(head.hash);
            }
            else {
                // First commit - empty previous state
                previousConfig = {};
            }
        }
        // Generate diff
        const diff = this.diffEngine.generateDiff(previousConfig, currentConfig);
        if (diff.length === 0) {
            throw new Error('No changes detected. Nothing to commit.');
        }
        // Get affected files
        const filesAffected = this.diffEngine.getAffectedFiles(diff);
        // Get referenced files (from $ref)
        let referencedFiles = [];
        try {
            const parser = new workflow_parser_1.WorkflowParser();
            const configPath = this.storage.getConfigPath();
            referencedFiles = parser.getReferencedFiles(configPath);
        }
        catch (_error) {
            // If we can't parse references, that's okay - just continue
        }
        // Get parent commit
        const index = this.storage.loadIndex();
        const parent = index.head;
        // Create commit object
        const commit = {
            hash: this.generateHash(diff, options.message),
            parent,
            timestamp: new Date().toISOString(),
            author: options.author || this.getDefaultAuthor(),
            message: options.message,
            diff,
            filesAffected,
            referencedFiles: referencedFiles.length > 0 ? referencedFiles : undefined,
        };
        // Save commit
        this.storage.saveCommit(commit);
        this.storage.updateIndex(commit);
        return commit;
    }
    /**
     * Get commit history
     */
    getHistory(limit) {
        return this.storage.getHistory(limit);
    }
    /**
     * Get a specific commit
     */
    getCommit(hash) {
        return this.storage.loadCommit(hash);
    }
    /**
     * Get current HEAD commit
     */
    getHead() {
        return this.storage.getHead();
    }
    /**
     * Reconstruct config state at a specific commit
     */
    async reconstructConfig(hash) {
        const commit = this.storage.loadCommit(hash);
        if (!commit) {
            throw new Error(`Commit not found: ${hash}`);
        }
        // Build commit chain from initial to target
        const commitChain = [];
        let current = commit;
        while (current) {
            commitChain.unshift(current);
            if (current.parent) {
                current = this.storage.loadCommit(current.parent);
            }
            else {
                current = null;
            }
        }
        // Apply diffs in order
        let config = {};
        for (const c of commitChain) {
            config = this.diffEngine.applyDiff(config, c.diff);
        }
        return config;
    }
    /**
     * Show diff for a commit
     */
    showCommitDiff(hash) {
        const commit = this.storage.loadCommit(hash);
        if (!commit) {
            return null;
        }
        const summary = this.diffEngine.getSummary(commit.diff);
        return { commit, summary };
    }
    /**
     * Get changes between two commits
     */
    async getChangesBetween(fromHash, toHash) {
        const fromConfig = await this.reconstructConfig(fromHash);
        const toConfig = await this.reconstructConfig(toHash);
        return this.diffEngine.generateDiff(fromConfig, toConfig);
    }
    /**
     * Revert to a specific commit (dangerous!)
     */
    async revertTo(hash) {
        // Reconstruct config at target commit
        const config = await this.reconstructConfig(hash);
        // Save as new config
        const configYaml = yaml.dump(config);
        this.storage.saveConfig(configYaml);
        // Note: This doesn't create a revert commit automatically
        // User should run "bangstack commit" after reverting
    }
    /**
     * Generate hash for commit
     */
    generateHash(diff, message) {
        const timestamp = Date.now().toString();
        const content = JSON.stringify({ diff, message, timestamp });
        return crypto.createHash('sha256').update(content).digest('hex').substring(0, 12);
    }
    /**
     * Get default author from git config or system
     */
    getDefaultAuthor() {
        // Try to get from git config
        try {
            const name = (0, child_process_1.execSync)('git config user.name', { encoding: 'utf-8' }).trim();
            const email = (0, child_process_1.execSync)('git config user.email', { encoding: 'utf-8' }).trim();
            return `${name} <${email}>`;
        }
        catch {
            // Fallback to system user
            return process.env.USER || process.env.USERNAME || 'unknown';
        }
    }
    /**
     * Get storage instance (for advanced usage)
     */
    getStorage() {
        return this.storage;
    }
    /**
     * Get diff engine instance (for advanced usage)
     */
    getDiffEngine() {
        return this.diffEngine;
    }
}
exports.CommitManager = CommitManager;
//# sourceMappingURL=commit-manager.js.map