"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.inspectRepositoryVisibility = inspectRepositoryVisibility;
exports.errorMessage = errorMessage;
exports.hashFile = hashFile;
exports.hashDirectory = hashDirectory;
exports.parseAdoptions = parseAdoptions;
exports.validateTargetEvidence = validateTargetEvidence;
const node_crypto_1 = require("node:crypto");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const errors_1 = require("../../errors");
const paths_1 = require("../../paths");
const planning_document_utils_1 = require("../planning-document-utils");
const contracts_1 = require("./contracts");
const repository_support_1 = require("./repository-support");
function inspectRepositoryVisibility(repositoryRoot, targetPath, exists) {
    if (!exists)
        return { state: "absent", ignoreRule: null };
    if ((0, repository_support_1.runGit)(repositoryRoot, ["ls-files", "--error-unmatch", "--", targetPath]) !== null) {
        return { state: "tracked", ignoreRule: null };
    }
    const ignored = (0, repository_support_1.runGit)(repositoryRoot, ["check-ignore", "-v", "--no-index", "--", targetPath]);
    if (ignored !== null) {
        const firstMatch = ignored.split(/\r?\n/, 1)[0];
        const exactRule = firstMatch.split("\t", 1)[0] || firstMatch;
        const pattern = exactRule.match(/^.*?:\d+:(.*)$/)?.[1] ?? "";
        if (!pattern.startsWith("!"))
            return { state: "ignored", ignoreRule: exactRule };
    }
    return { state: "untracked", ignoreRule: null };
}
function errorMessage(error) {
    return error instanceof Error && error.message.trim() !== ""
        ? error.message
        : "Baseline planning could not inspect this target with bounded repository-local evidence.";
}
function hashFile(filePath) {
    return `sha256:${(0, node_crypto_1.createHash)("sha256").update(node_fs_1.default.readFileSync(filePath)).digest("hex")}`;
}
function hashDirectory(directoryPath) {
    const files = [];
    const pending = [directoryPath];
    let totalBytes = 0;
    while (pending.length > 0) {
        const directory = pending.pop();
        for (const name of node_fs_1.default.readdirSync(directory).sort((left, right) => right.localeCompare(left, "en"))) {
            const entryPath = node_path_1.default.join(directory, name);
            const stat = node_fs_1.default.lstatSync(entryPath);
            if (stat.isSymbolicLink())
                continue;
            if (stat.isDirectory()) {
                pending.push(entryPath);
                continue;
            }
            if (!stat.isFile())
                continue;
            files.push(entryPath);
            totalBytes += stat.size;
            if (files.length > contracts_1.MAX_HASHED_FILES || totalBytes > contracts_1.MAX_HASHED_BYTES) {
                throw (0, errors_1.createUsageError)(`Declared target directory '${directoryPath}' exceeds the bounded baseline hashing limit.`);
            }
        }
    }
    const hash = (0, node_crypto_1.createHash)("sha256");
    for (const file of files.sort((left, right) => left.localeCompare(right, "en"))) {
        const relative = (0, planning_document_utils_1.toRepositoryLikePath)(node_path_1.default.relative(directoryPath, file));
        const bytes = node_fs_1.default.readFileSync(file);
        hash.update(`${relative.length}:${relative}:${bytes.byteLength}:`);
        hash.update(bytes);
        hash.update("\n");
    }
    return `sha256:${hash.digest("hex")}`;
}
function parseAdoptions(values, declaredTargets) {
    const manualAdoptions = new Map();
    const acceptedRecoverySources = new Map();
    const declared = new Set(declaredTargets);
    for (const value of values) {
        const separator = value.indexOf("::");
        if (separator <= 0) {
            throw (0, errors_1.createUsageError)(`Invalid --adopt value '${value}'.`, ["Use --adopt <declared-path>::<concrete-rationale>."]);
        }
        const targetPath = (0, paths_1.parseRepositoryRelativePath)(value.slice(0, separator).trim()).normalizedPath;
        const rationale = value.slice(separator + 2).trim();
        if (!declared.has(targetPath)) {
            throw (0, errors_1.createUsageError)(`Adoption path '${targetPath}' is not a declared task target.`);
        }
        if (rationale.length < 12 || /^(tbd|todo|pending|adopted)$/i.test(rationale)) {
            throw (0, errors_1.createUsageError)(`Adoption path '${targetPath}' requires a concrete non-placeholder rationale.`);
        }
        if (manualAdoptions.has(targetPath) || acceptedRecoverySources.has(targetPath)) {
            throw (0, errors_1.createUsageError)(`Adoption path '${targetPath}' was provided more than once.`);
        }
        if (rationale.startsWith("accepted-predecessor:")) {
            const source = rationale.slice("accepted-predecessor:".length).trim();
            const sourcePath = (0, paths_1.parseRepositoryRelativePath)(source).normalizedPath;
            if (!sourcePath.endsWith("/task-definition.md")) {
                throw (0, errors_1.createUsageError)(`Accepted predecessor recovery source '${sourcePath}' must select a task-definition.md artifact.`);
            }
            acceptedRecoverySources.set(targetPath, sourcePath);
        }
        else {
            manualAdoptions.set(targetPath, rationale);
        }
    }
    return { manualAdoptions, acceptedRecoverySources };
}
function validateTargetEvidence(targets) {
    const issues = [];
    const seen = new Set();
    for (const target of targets) {
        try {
            (0, paths_1.parseRepositoryRelativePath)(target.path);
        }
        catch {
            issues.push(`Declared baseline target '${target.path}' is not repository-relative.`);
        }
        if (seen.has(target.path))
            issues.push(`Declared baseline target '${target.path}' is duplicated.`);
        seen.add(target.path);
        if (target.exists !== (target.kind !== "absent"))
            issues.push(`Target '${target.path}' has contradictory existence/kind evidence.`);
        if (!["tracked", "untracked", "ignored", "absent"].includes(target.repositoryVisibility)) {
            issues.push(`Target '${target.path}' has invalid repository visibility '${target.repositoryVisibility}'.`);
        }
        if (target.repositoryVisibility === "absent" && target.exists) {
            issues.push(`Visible target '${target.path}' cannot be classified absent.`);
        }
        if (target.repositoryVisibility !== "absent" && !target.exists) {
            issues.push(`Absent target '${target.path}' cannot be classified ${target.repositoryVisibility}.`);
        }
        if (target.repositoryVisibility === "ignored" && (target.ignoreRule === null || target.ignoreRule.trim() === "")) {
            issues.push(`Ignored target '${target.path}' requires its matching ignore rule.`);
        }
        if (target.repositoryVisibility !== "ignored" && target.ignoreRule !== null) {
            issues.push(`Non-ignored target '${target.path}' must not carry an ignore rule.`);
        }
        if (target.exists && (target.contentSha256 === null || !contracts_1.CONTENT_HASH_PATTERN.test(target.contentSha256))) {
            issues.push(`Target '${target.path}' requires a sha256 content hash.`);
        }
        if (!target.exists && target.contentSha256 !== null)
            issues.push(`Absent target '${target.path}' must not carry a content hash.`);
        if (!["clean", "pre-existing-unattributed", "adopted-pre-staged", "accepted-predecessor"]
            .includes(target.preExistingClassification)) {
            issues.push(`Target '${target.path}' has invalid pre-existing classification '${target.preExistingClassification}'.`);
        }
        if (target.preExistingClassification === "clean" && target.dirty)
            issues.push(`Dirty target '${target.path}' cannot be classified clean.`);
        if (target.preExistingClassification === "adopted-pre-staged" && target.adoptionRationale.trim().length < 12) {
            issues.push(`Adopted target '${target.path}' requires a concrete adoption rationale.`);
        }
        if (target.preExistingClassification !== "adopted-pre-staged" && target.adoptionRationale !== "") {
            issues.push(`Non-adopted target '${target.path}' must not carry adoption rationale.`);
        }
        if (target.preExistingClassification === "accepted-predecessor") {
            const predecessor = target.acceptedPredecessor;
            if (!target.dirty || predecessor === null) {
                issues.push(`Accepted-predecessor target '${target.path}' requires dirty state and complete predecessor provenance.`);
            }
            else {
                if (predecessor.pathDigest !== target.contentSha256) {
                    issues.push(`Accepted-predecessor target '${target.path}' must preserve the exact predecessor path digest.`);
                }
                if (!contracts_1.CONTENT_HASH_PATTERN.test(predecessor.pathDigest)) {
                    issues.push(`Accepted-predecessor target '${target.path}' requires a sha256 path digest.`);
                }
                if (!contracts_1.SOURCE_REVISION_PATTERN.test(predecessor.sourceRevision)) {
                    issues.push(`Accepted-predecessor target '${target.path}' requires an exact source revision.`);
                }
                if (!/^implementation-summary-review-[a-f0-9]{24}$/.test(predecessor.reviewReceiptId)) {
                    issues.push(`Accepted-predecessor target '${target.path}' requires a canonical review receipt identity.`);
                }
                if (!/^[a-f0-9]{64}$/.test(predecessor.implementationSummaryRevision)) {
                    issues.push(`Accepted-predecessor target '${target.path}' requires an exact summary revision.`);
                }
            }
        }
        else if (target.acceptedPredecessor !== null) {
            issues.push(`Non-predecessor target '${target.path}' must not carry accepted-predecessor provenance.`);
        }
    }
    return issues;
}
