import test from "node:test";
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import os from "node:os";
import path from "node:path";
import { promises as fs } from "node:fs";
import { bootstrapTarget } from "@shrey-junior/sj-core/core/bootstrap.js";
import { loadCanonicalConfig } from "@shrey-junior/sj-core/core/config.js";
import { getRuntimeSnapshot } from "@shrey-junior/sj-core/runtime/client.js";
import { readJson } from "@shrey-junior/sj-core/utils/fs.js";
import { runAgentPack } from "../commands/agent-pack.js";
import { runPack } from "../commands/pack.js";
import { repoRoot } from "./helpers/desktop-launch.js";
function jsonLogger(logs) {
    return {
        info(message) {
            logs.push(message);
        },
        warn() { },
        error() { }
    };
}
test("pack status/set/clear updates runtime-backed pack policy and exported artifacts", async () => {
    const repoRootPath = repoRoot();
    const config = await loadCanonicalConfig(repoRootPath);
    const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "sj-pack-"));
    const target = path.join(tempDir, "portable-repo");
    await fs.mkdir(target, { recursive: true });
    await fs.writeFile(path.join(target, "package.json"), '{\n  "name": "portable-repo"\n}\n', "utf8");
    await bootstrapTarget({
        repoRoot: repoRootPath,
        targetRoot: target
    }, config);
    await runAgentPack({
        repoRoot: repoRootPath,
        targetRoot: target,
        json: false,
        logger: jsonLogger([])
    });
    const beforeLogs = [];
    const beforeExit = await runPack({
        repoRoot: repoRootPath,
        targetRoot: target,
        action: "status",
        json: true,
        logger: jsonLogger(beforeLogs)
    });
    assert.equal(beforeExit, 0);
    const before = JSON.parse(beforeLogs.join("\n"));
    assert.equal(before.selectedPackSource, "heuristic-default");
    assert.equal(before.explicitSelection, null);
    const setLogs = [];
    const setExit = await runPack({
        repoRoot: repoRootPath,
        targetRoot: target,
        action: "set",
        packId: "research-pack",
        json: true,
        logger: jsonLogger(setLogs)
    });
    assert.equal(setExit, 0);
    const setPayload = JSON.parse(setLogs.join("\n"));
    assert.equal(setPayload.changed, true);
    assert.equal(setPayload.selectedPack, "research-pack");
    assert.equal(setPayload.selectedPackSource, "runtime-explicit");
    assert.equal(setPayload.explicitSelection, "research-pack");
    assert.deepEqual(setPayload.effectiveCapabilityIds.includes("exa"), true);
    assert.deepEqual(setPayload.effectiveCapabilityIds.includes("playwright"), false);
    assert.equal(setPayload.executionRevision > before.executionRevision, true);
    const selectedPackArtifact = await readJson(path.join(target, setPayload.artifactPaths.selectedPack));
    assert.equal(selectedPackArtifact.selectedPack, "research-pack");
    assert.equal(selectedPackArtifact.selectedPackSource, "runtime-explicit");
    assert.equal(selectedPackArtifact.explicitSelection, "research-pack");
    const agentPack = await readJson(path.join(target, ".agent", "context", "agent-pack.json"));
    const repoMap = await readJson(path.join(target, ".agent", "context", "repo-map.json"));
    const readySubstrate = await readJson(path.join(target, ".agent", "state", "ready-substrate.json"));
    assert.equal(agentPack.packSelection?.selectedPack, "research-pack");
    assert.equal(repoMap.packSelection?.selectedPack, "research-pack");
    assert.equal(readySubstrate.packSelection?.selectedPack, "research-pack");
    assert.equal(readySubstrate.packSelection?.selectedPackSource, "runtime-explicit");
    const beforeRepeatSetHashes = await hashFiles(target, trackedArtifacts);
    const idempotentLogs = [];
    const idempotentExit = await runPack({
        repoRoot: repoRootPath,
        targetRoot: target,
        action: "set",
        packId: "research-pack",
        json: true,
        logger: jsonLogger(idempotentLogs)
    });
    assert.equal(idempotentExit, 0);
    const idempotentPayload = JSON.parse(idempotentLogs.join("\n"));
    assert.equal(idempotentPayload.changed, false);
    assert.equal(idempotentPayload.selectedPack, "research-pack");
    const afterRepeatSnapshot = await getRuntimeSnapshot(target);
    assert.equal(afterRepeatSnapshot.revision, setPayload.executionRevision);
    assert.equal(idempotentPayload.executionRevision, setPayload.executionRevision);
    const afterRepeatSetHashes = await hashFiles(target, trackedArtifacts);
    assert.deepEqual(afterRepeatSetHashes, beforeRepeatSetHashes);
    const clearLogs = [];
    const clearExit = await runPack({
        repoRoot: repoRootPath,
        targetRoot: target,
        action: "clear",
        json: true,
        logger: jsonLogger(clearLogs)
    });
    assert.equal(clearExit, 0);
    const clearPayload = JSON.parse(clearLogs.join("\n"));
    assert.equal(clearPayload.changed, true);
    assert.equal(clearPayload.selectedPackSource, "heuristic-default");
    assert.equal(clearPayload.explicitSelection, null);
    assert.equal(clearPayload.executionRevision > setPayload.executionRevision, true);
    const afterClearReferenceHashes = await hashFiles(target, trackedArtifacts);
    const clearAgainSnapshotBefore = await getRuntimeSnapshot(target);
    assert.equal(clearAgainSnapshotBefore.revision, clearPayload.executionRevision);
    const clearAgainLogs = [];
    const clearAgainExit = await runPack({
        repoRoot: repoRootPath,
        targetRoot: target,
        action: "clear",
        json: true,
        logger: jsonLogger(clearAgainLogs)
    });
    assert.equal(clearAgainExit, 0);
    const clearAgainPayload = JSON.parse(clearAgainLogs.join("\n"));
    assert.equal(clearAgainPayload.changed, false);
    assert.equal(clearAgainPayload.executionRevision, clearPayload.executionRevision);
    const afterClearAgainHashes = await hashFiles(target, trackedArtifacts);
    assert.deepEqual(afterClearAgainHashes, afterClearReferenceHashes);
    const beforeStatusHashes = await hashFiles(target, trackedArtifacts);
    const statusLogs = [];
    const statusExit = await runPack({
        repoRoot: repoRootPath,
        targetRoot: target,
        action: "status",
        json: true,
        logger: jsonLogger(statusLogs)
    });
    assert.equal(statusExit, 0);
    const afterStatusHashes = await hashFiles(target, trackedArtifacts);
    assert.deepEqual(afterStatusHashes, beforeStatusHashes);
});
test("blocked packs return an exact unavailable reason", async () => {
    const repoRootPath = repoRoot();
    const config = await loadCanonicalConfig(repoRootPath);
    const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "sj-pack-blocked-"));
    const target = path.join(tempDir, "portable-repo");
    await fs.mkdir(target, { recursive: true });
    await fs.writeFile(path.join(target, "package.json"), '{\n  "name": "portable-repo"\n}\n', "utf8");
    await bootstrapTarget({
        repoRoot: repoRootPath,
        targetRoot: target
    }, config);
    const logs = [];
    const exitCode = await runPack({
        repoRoot: repoRootPath,
        targetRoot: target,
        action: "set",
        packId: "aws-pack",
        json: true,
        logger: jsonLogger(logs)
    });
    assert.equal(exitCode, 1);
    const payload = JSON.parse(logs.join("\n"));
    assert.equal(payload.ok, false);
    assert.match(payload.unavailablePackReason ?? "", /AWS Pack is blocked/i);
    assert.equal(payload.availablePacks.some((entry) => entry.id === "aws-pack" && entry.executable), false);
});
async function hashFiles(targetRoot, relativePaths) {
    const hashes = {};
    for (const relativePath of relativePaths) {
        const fullPath = path.join(targetRoot, relativePath);
        try {
            const content = await fs.readFile(fullPath);
            hashes[relativePath] = createHash("sha256").update(content).digest("hex");
        }
        catch {
            hashes[relativePath] = null;
        }
    }
    return hashes;
}
const trackedArtifacts = [
    ".agent/state/selected-pack.json",
    ".agent/state/ready-substrate.json",
    ".agent/context/agent-pack.json",
    ".agent/context/repo-map.json"
];
