"use strict";
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;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegistryDiffEngine = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const chalk_1 = __importDefault(require("chalk"));
class RegistryDiffEngine {
    constructor(projectRoot = process.cwd()) {
        this.projectRoot = projectRoot;
    }
    async diff(registry) {
        const { RegistryBuilder } = await Promise.resolve().then(() => __importStar(require("./RegistryBuilder")));
        const builder = new RegistryBuilder(this.projectRoot);
        const currentRegistry = registry || (await builder.read());
        const currentFiles = builder["discoverFiles"]();
        const items = [];
        const fileSet = new Set(currentFiles);
        const seenFiles = new Set();
        const counts = { new: 0, missing: 0, changed: 0, orphan: 0, untracked: 0 };
        if (!currentRegistry) {
            items.push({
                type: "untracked",
                filePath: ".mycontext/file-registry.json",
                detail: "No file registry found. Run 'mycontext registry:update' first.",
                severity: "error",
            });
            return {
                timestamp: new Date().toISOString(),
                summary: "No registry file exists",
                items,
                counts: { ...counts, total: 1 },
                hasUntrackedFiles: true,
                hasOrphanRefs: false,
            };
        }
        // 1. Check registry entries against disk
        for (const [filePath, entry] of Object.entries(currentRegistry.files)) {
            seenFiles.add(filePath);
            if (!fileSet.has(filePath)) {
                items.push({
                    type: "missing",
                    filePath,
                    detail: `File in registry but not on disk. ${entry.ref ? `Referenced by: ${entry.ref.join(", ")}` : ""}`,
                    severity: "warning",
                });
                counts.missing++;
                continue;
            }
            // Check hash for tracked/untracked files
            if ((entry.status === "tracked" || entry.status === "untracked") &&
                entry.hash) {
                const builder = new RegistryBuilder(this.projectRoot);
                const currentHash = await builder["hashFile"](filePath);
                if (currentHash !== entry.hash) {
                    items.push({
                        type: "changed",
                        filePath,
                        detail: `Content changed (hash: ${entry.hash} → ${currentHash})`,
                        severity: "info",
                    });
                    counts.changed++;
                }
            }
        }
        // 2. Check disk files not in registry
        for (const filePath of currentFiles) {
            if (seenFiles.has(filePath))
                continue;
            const ext = path.extname(filePath).toLowerCase();
            const sourceExts = new Set([
                ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
                ".json", ".yaml", ".yml", ".toml",
                ".css", ".scss", ".less",
                ".md", ".mdx",
                ".graphql", ".prisma",
            ]);
            if (sourceExts.has(ext)) {
                items.push({
                    type: "new",
                    filePath,
                    detail: "Source file exists on disk but not in file registry",
                    severity: "warning",
                });
                counts.new++;
            }
        }
        // 3. Check context.json ownership paths exist
        const orphanRefs = await this.findOrphanRefs();
        for (const ref of orphanRefs) {
            items.push({
                type: "orphan",
                filePath: ref,
                detail: "Referenced in context.json but file does not exist on disk",
                severity: "warning",
            });
            counts.orphan++;
        }
        // 4. Find untracked files
        for (const [filePath, entry] of Object.entries(currentRegistry.files)) {
            if (entry.status === "untracked" && fileSet.has(filePath)) {
                items.push({
                    type: "untracked",
                    filePath,
                    detail: "File exists but is not tracked in context.json",
                    severity: "warning",
                });
                counts.untracked++;
            }
        }
        const total = items.length;
        return {
            timestamp: new Date().toISOString(),
            summary: this.buildSummary(counts, total),
            items,
            counts: { ...counts, total },
            hasUntrackedFiles: counts.untracked > 0 || counts.new > 0,
            hasOrphanRefs: counts.orphan > 0,
        };
    }
    async findOrphanRefs() {
        const orphans = [];
        try {
            const brain = await fs.readJson(path.join(this.projectRoot, ".mycontext", "context.json"));
            const features = brain.features || brain.phases?.implementation_plan?.features || [];
            for (const feature of features) {
                const ownership = feature.ownership || {};
                for (const key of ["routes", "components", "actions", "models", "permissions", "i18n", "tests"]) {
                    const paths = ownership[key] || [];
                    for (const p of paths) {
                        const cleaned = p.replace(/^\//, "");
                        if (!(await fs.pathExists(path.join(this.projectRoot, cleaned)))) {
                            orphans.push(cleaned);
                        }
                    }
                }
            }
        }
        catch {
            // No context.json
        }
        return orphans;
    }
    buildSummary(counts, total) {
        const parts = [];
        if (total === 0)
            return "No drift detected. Registry is clean.";
        if (counts.new > 0)
            parts.push(`${counts.new} new`);
        if (counts.missing > 0)
            parts.push(`${counts.missing} missing`);
        if (counts.changed > 0)
            parts.push(`${counts.changed} changed`);
        if (counts.orphan > 0)
            parts.push(`${counts.orphan} orphan refs`);
        if (counts.untracked > 0)
            parts.push(`${counts.untracked} untracked`);
        return `${parts.join(", ")} — ${total} total items`;
    }
    displayReport(report) {
        console.log(chalk_1.default.cyan("\n┌──────────────────────────────────────────────┐"));
        console.log(chalk_1.default.cyan("│") + chalk_1.default.bold.white("  📋 File Registry Diff") + " ".repeat(25) + chalk_1.default.cyan("│"));
        console.log(chalk_1.default.cyan("├──────────────────────────────────────────────┤"));
        console.log(chalk_1.default.cyan("│") + chalk_1.default.gray(`  ${report.summary.substring(0, 43)}`.padEnd(45)) + chalk_1.default.cyan("│"));
        console.log(chalk_1.default.cyan("└──────────────────────────────────────────────┘"));
        if (report.items.length === 0) {
            console.log(chalk_1.default.green("\n✓ No drift detected.\n"));
            return;
        }
        const grouped = {
            new: report.items.filter((i) => i.type === "new"),
            missing: report.items.filter((i) => i.type === "missing"),
            changed: report.items.filter((i) => i.type === "changed"),
            orphan: report.items.filter((i) => i.type === "orphan"),
            untracked: report.items.filter((i) => i.type === "untracked"),
        };
        for (const [type, items] of Object.entries(grouped)) {
            if (items.length === 0)
                continue;
            const icons = {
                new: "🆕",
                missing: "❌",
                changed: "🔄",
                orphan: "🔗",
                untracked: "⚠️",
            };
            console.log(`\n${icons[type] || "•"} ${type.toUpperCase()} (${items.length}):`);
            for (const item of items.slice(0, 10)) {
                console.log(`   ${chalk_1.default.gray(item.filePath)}`);
                console.log(`     ${item.detail}`);
            }
            if (items.length > 10) {
                console.log(`   ${chalk_1.default.gray(`... and ${items.length - 10} more`)}`);
            }
        }
        if (report.hasUntrackedFiles) {
            console.log(chalk_1.default.yellow("\n⚠️  Untracked files detected. Run 'mycontext registry:update' to update the registry.\n"));
        }
    }
}
exports.RegistryDiffEngine = RegistryDiffEngine;
//# sourceMappingURL=RegistryDiffEngine.js.map