"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.inspectTaskScaffoldRecoveryPairSync = inspectTaskScaffoldRecoveryPairSync;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const scaffold_application_1 = require("./scaffold-application");
/**
 * Classifies only an exact untouched CLI-generated task pair as recoverable.
 * Complete byte equality is the final authority; the explicit lifecycle
 * checks make rejection guidance precise before any transaction is attempted.
 */
function inspectTaskScaffoldRecoveryPairSync(options) {
    const issues = [];
    const taskDefinitionText = readRequiredFile(options.taskDefinitionPath, "task definition", issues);
    const implementationSummaryText = readRequiredFile(options.implementationSummaryPath, "implementation summary", issues);
    const expectedPendingImplementationSummaryTexts = uniqueValues([
        ...(options.expectedPendingImplementationSummaryTexts ?? []),
        ...(options.expectedPendingImplementationSummaryText === undefined
            ? []
            : [options.expectedPendingImplementationSummaryText])
    ]);
    if (taskDefinitionText !== null) {
        const normalizedTaskDefinitionText = dematerializeCanonicalPlanningLinks(taskDefinitionText, options.taskDefinitionRepositoryRelativePath);
        const normalizedLegacyTaskDefinitionTexts = options.expectedLegacyTaskDefinitionTexts.map((candidate) => dematerializeCanonicalPlanningLinks(candidate, options.taskDefinitionRepositoryRelativePath));
        const normalizedRecoveredTaskDefinitionText = options.expectedRecoveredTaskDefinitionText === undefined
            ? undefined
            : dematerializeCanonicalPlanningLinks(options.expectedRecoveredTaskDefinitionText, options.taskDefinitionRepositoryRelativePath);
        requireTopLevelScalar(taskDefinitionText, "status", "draft", "Task definition", issues);
        requireTopLevelScalar(taskDefinitionText, "readiness", "needs_more_evidence", "Task definition", issues);
        requireTopLevelScalar(taskDefinitionText, "workflow_provenance", "cli", "Task definition", issues);
        requireTaskNewCommand(taskDefinitionText, "Task definition", issues);
        if (!normalizedLegacyTaskDefinitionTexts.includes(normalizedTaskDefinitionText)
            && normalizedTaskDefinitionText !== normalizedRecoveredTaskDefinitionText) {
            const closestDifference = closestTextDifference(normalizedTaskDefinitionText, [
                ...normalizedLegacyTaskDefinitionTexts,
                ...(normalizedRecoveredTaskDefinitionText === undefined
                    ? []
                    : [normalizedRecoveredTaskDefinitionText])
            ]);
            issues.push(`Task definition bytes do not match the exact legacy CLI-generated scaffold; authored or otherwise changed content is never recoverable through this command.${closestDifference === null ? "" : ` First difference: ${closestDifference}`}`);
        }
    }
    if (implementationSummaryText !== null) {
        requireTopLevelScalar(implementationSummaryText, "workflow_provenance", "cli", "Implementation summary", issues);
        requireTopLevelScalar(implementationSummaryText, "workflow_manual_exception", "", "Implementation summary", issues);
        requireTopLevelScalar(implementationSummaryText, "current_attempt_id", "null", "Implementation summary", issues);
        requireTopLevelScalar(implementationSummaryText, "attempts", "[]", "Implementation summary", issues);
        rejectTopLevelField(implementationSummaryText, "implementation_status", "Implementation summary", issues);
        rejectTopLevelField(implementationSummaryText, "status", "Implementation summary", issues);
        requireTaskNewCommand(implementationSummaryText, "Implementation summary", issues);
        if (!expectedPendingImplementationSummaryTexts.includes(implementationSummaryText)) {
            issues.push("Implementation summary bytes do not match the exact default pending CLI-generated artifact; attempt, evidence, or authored content is never recoverable through this command.");
        }
    }
    const alreadyRecovered = taskDefinitionText !== null
        && implementationSummaryText !== null
        && dematerializeCanonicalPlanningLinks(taskDefinitionText, options.taskDefinitionRepositoryRelativePath) === (options.expectedRecoveredTaskDefinitionText === undefined
            ? undefined
            : dematerializeCanonicalPlanningLinks(options.expectedRecoveredTaskDefinitionText, options.taskDefinitionRepositoryRelativePath))
        && expectedPendingImplementationSummaryTexts.includes(implementationSummaryText);
    const eligible = issues.length === 0;
    return {
        eligible,
        action: !eligible ? "blocked" : alreadyRecovered ? "no-write" : "replace",
        issues: uniqueValues(issues),
        taskDefinitionText,
        implementationSummaryText,
        taskDefinitionExpectedRevision: taskDefinitionText === null
            ? null
            : (0, scaffold_application_1.planningScaffoldContentRevision)(taskDefinitionText),
        implementationSummaryExpectedRevision: implementationSummaryText === null
            ? null
            : (0, scaffold_application_1.planningScaffoldContentRevision)(implementationSummaryText)
    };
}
function closestTextDifference(observed, candidates) {
    let closest = null;
    for (const candidate of candidates) {
        let sharedPrefix = 0;
        const limit = Math.min(observed.length, candidate.length);
        while (sharedPrefix < limit
            && observed.charCodeAt(sharedPrefix) === candidate.charCodeAt(sharedPrefix)) {
            sharedPrefix += 1;
        }
        const line = observed.slice(0, sharedPrefix).split("\n").length;
        const observedLine = observed.split(/\r?\n/u)[line - 1] ?? "<end of file>";
        const expectedLine = candidate.split(/\r?\n/u)[line - 1] ?? "<end of file>";
        const message = `line ${line}, observed ${JSON.stringify(observedLine)}, expected ${JSON.stringify(expectedLine)}.`;
        if (closest === null || sharedPrefix > closest.sharedPrefix) {
            closest = { sharedPrefix, message };
        }
    }
    return closest?.message ?? null;
}
function dematerializeCanonicalPlanningLinks(text, artifactPath) {
    const artifactDirectory = node_path_1.default.posix.dirname(artifactPath);
    return text.replace(/\[([A-Za-z0-9._@/-]+\/[A-Za-z0-9._@/-]+)\]\(([^)\n]+)\)/gu, (match, label, destination) => {
        if (!isRepositoryRelativePath(label))
            return match;
        const expectedDestination = node_path_1.default.posix.relative(artifactDirectory, label);
        return destination === expectedDestination ? label : match;
    });
}
function isRepositoryRelativePath(value) {
    return value !== ""
        && !value.startsWith("/")
        && !value.includes("\\")
        && value.split("/").every((segment) => segment !== "" && segment !== "." && segment !== "..");
}
function readRequiredFile(filePath, label, issues) {
    const stats = node_fs_1.default.statSync(filePath, { throwIfNoEntry: false });
    if (stats === undefined) {
        issues.push(`The ${label} '${filePath}' is missing; recovery requires an existing complete pair.`);
        return null;
    }
    if (!stats.isFile()) {
        issues.push(`The ${label} '${filePath}' is not a regular file.`);
        return null;
    }
    return node_fs_1.default.readFileSync(filePath, "utf8");
}
function requireTopLevelScalar(text, field, expected, label, issues) {
    const observed = readTopLevelScalar(text, field);
    if (observed !== expected) {
        issues.push(`${label} must retain ${field}: ${expected}; found ${observed === null ? "<missing>" : JSON.stringify(observed)}.`);
    }
}
function rejectTopLevelField(text, field, label, issues) {
    const observed = readTopLevelScalar(text, field);
    if (observed !== null) {
        issues.push(`${label} must not contain lifecycle field '${field}' before scaffold recovery; found ${JSON.stringify(observed)}.`);
    }
}
function readTopLevelScalar(text, field) {
    const escapedField = field.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
    const match = text.match(new RegExp(`^${escapedField}:\\s*(.*)$`, "mu"));
    return match === null ? null : parseScalar(match[1]);
}
function requireTaskNewCommand(text, label, issues) {
    const observed = readTopLevelScalar(text, "workflow_cli_command");
    if (observed === null || !/^day-shift task new(?:\s|$)/u.test(observed)) {
        issues.push(`${label} must retain a CLI generation command beginning with 'day-shift task new'.`);
    }
}
function parseScalar(value) {
    const trimmed = value.trim();
    if ((trimmed.startsWith('"') && trimmed.endsWith('"'))
        || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
        try {
            return trimmed.startsWith('"')
                ? JSON.parse(trimmed)
                : trimmed.slice(1, -1).replace(/''/gu, "'");
        }
        catch {
            return trimmed;
        }
    }
    return trimmed;
}
function uniqueValues(values) {
    return [...new Set(values)];
}
