// Context assembly for the generation call (ADR-0008 D1). Delegates to #5 buildContextPack so the
// target file, nearby test files, manifests/config, and type definitions are selected, ranked, and
// excerpted under a byte budget — every excerpt already redacted by #5 at the IO boundary. Pure
// except for the WorkspaceFs seam threaded into buildContextPack. The `task` hint is a forward-
// compatible natural-language description of the target for a future embedding ranker; the Wave-1
// lexical strategy tolerates it.
import { buildContextPack, DEFAULT_DISCOVERY_OPTIONS, lexicalRetrievalStrategy, SELECTION_REASON_PRIORITY, } from "@oscharko-dev/keiko-workspace";
import { nodeWorkspaceFs } from "@oscharko-dev/keiko-workspace/internal/fs";
function toPosix(path) {
    return path.split("\\").join("/");
}
function underDir(path, dir) {
    const normalized = toPosix(dir);
    return path === normalized || path.startsWith(`${normalized}/`);
}
function basename(path) {
    const normalized = toPosix(path);
    const idx = normalized.lastIndexOf("/");
    return idx === -1 ? normalized : normalized.slice(idx + 1);
}
function stem(path) {
    const base = basename(path);
    const idx = base.lastIndexOf(".");
    return idx <= 0 ? base : base.slice(0, idx);
}
function testStem(path) {
    const parts = stem(path).split(".");
    const marker = parts.findIndex((part) => part === "test" || part === "spec");
    return marker === -1 ? parts.join(".") : parts.slice(0, marker).join(".");
}
function targetPaths(target) {
    if (target.kind === "file") {
        return [toPosix(target.filePath)];
    }
    if (target.kind === "changedFiles") {
        return target.filePaths.map(toPosix);
    }
    return [];
}
function moduleDir(target) {
    return target.kind === "module" ? toPosix(target.moduleDir) : undefined;
}
function isRequestedTarget(path, input) {
    const module = moduleDir(input.target);
    return (targetPaths(input.target).includes(path) || (module !== undefined && underDir(path, module)));
}
function isTestCandidate(path, workspace, selectionReason) {
    return (selectionReason === "test" ||
        workspace.testDirs.some((testDir) => underDir(path, testDir)) ||
        stem(path)
            .split(".")
            .some((part) => part === "test" || part === "spec"));
}
function isNearbyTest(path, workspace, input, selectionReason) {
    if (!isTestCandidate(path, workspace, selectionReason)) {
        return false;
    }
    const targets = targetPaths(input.target);
    if (targets.length === 0) {
        const module = moduleDir(input.target);
        return module !== undefined && path.includes(basename(module));
    }
    const candidateStem = testStem(path);
    return targets.some((target) => candidateStem === stem(target));
}
function priorityIndex(reason) {
    return SELECTION_REASON_PRIORITY.indexOf(reason);
}
function issue8Priority(ranked, workspace, input) {
    const path = toPosix(ranked.file.relativePath);
    if (isRequestedTarget(path, input)) {
        return 0;
    }
    if (isNearbyTest(path, workspace, input, ranked.selectionReason)) {
        return 1;
    }
    return 2;
}
function isSupportContext(ranked) {
    return ranked.selectionReason === "manifest" || ranked.selectionReason === "config";
}
function focusedContext(ranked, workspace, input) {
    const focused = ranked.filter((item) => issue8Priority(item, workspace, input) < 2 || isSupportContext(item));
    return focused.length === 0 ? ranked : focused;
}
function createUnitTestRetrievalStrategy(workspace, input) {
    return {
        rank: (files, task) => {
            const ranked = focusedContext(lexicalRetrievalStrategy.rank(files, task), workspace, input);
            return [...ranked].sort((a, b) => {
                const byIssue8 = issue8Priority(a, workspace, input) - issue8Priority(b, workspace, input);
                if (byIssue8 !== 0) {
                    return byIssue8;
                }
                const byReason = priorityIndex(a.selectionReason) - priorityIndex(b.selectionReason);
                if (byReason !== 0) {
                    return byReason;
                }
                return a.file.relativePath.localeCompare(b.file.relativePath);
            });
        },
    };
}
function taskHint(target) {
    if (target.kind === "file") {
        return target.targetFunction === undefined
            ? `unit tests for ${target.filePath}`
            : `unit tests for ${target.targetFunction} in ${target.filePath}`;
    }
    if (target.kind === "module") {
        return `unit tests for module ${target.moduleDir}`;
    }
    return `unit tests for ${target.filePaths.join(", ")}`;
}
export function buildTestGenContext(workspace, input, limits, deps = {}) {
    const request = {
        task: taskHint(input.target),
        budgetBytes: limits.contextBudgetBytes,
        maxBytesPerFile: limits.maxBytesPerFile,
        discovery: DEFAULT_DISCOVERY_OPTIONS,
    };
    // buildContextPack defaults to the lexical strategy, but ContextPackDeps requires the field; pass
    // the #5 barrel default explicitly so we hand a complete deps object without reaching past #5.
    const packDeps = {
        fs: deps.fs ?? nodeWorkspaceFs,
        strategy: createUnitTestRetrievalStrategy(workspace, input),
    };
    return buildContextPack(workspace, request, packDeps);
}
