"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runIgnoreRecommendationCheck = runIgnoreRecommendationCheck;
exports.runMarkdownlintRecommendationCheck = runMarkdownlintRecommendationCheck;
exports.runSandboxParentPathIdentityCheck = runSandboxParentPathIdentityCheck;
exports.runSandboxPathHygieneCheck = runSandboxPathHygieneCheck;
exports.runSandboxCleanupReadinessCheck = runSandboxCleanupReadinessCheck;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const workspace_ignore_1 = require("../workspace-ignore");
const default_assets_1 = require("../workspace-init/default-assets");
const sandbox_contract_1 = require("../paths/sandbox-contract");
const local_paths_1 = require("./local-paths");
function runIgnoreRecommendationCheck(repositoryRoot, findings) {
    const preview = (0, workspace_ignore_1.createIgnoreInstallPreview)(repositoryRoot);
    if (preview.proposedAdditions.length > 0) {
        findings.push({
            code: "IGNORE_RULES_RECOMMENDED",
            message: `Recommended local ignore entries are missing (${preview.proposedAdditions.join(", ")}); run 'day-shift ignore install'.`,
            severity: "warning",
            path: ".gitignore"
        });
        return {
            id: "ignore-rules-recommendation",
            status: "warning",
            message: "Recommended local ignore entries are missing."
        };
    }
    return {
        id: "ignore-rules-recommendation",
        status: "ok",
        message: "Recommended local ignore entries are installed."
    };
}
function runMarkdownlintRecommendationCheck(repositoryRoot, findings) {
    const markdownlintRelativePath = ".markdownlint.json";
    const markdownlintPath = node_path_1.default.join(repositoryRoot, markdownlintRelativePath);
    if (!(0, local_paths_1.isExistingFile)(markdownlintPath)) {
        findings.push({
            code: "MARKDOWNLINT_RECOMMENDED",
            message: "Root .markdownlint.json is missing; run 'day-shift init' to scaffold the recommended Markdown lint config.",
            severity: "warning",
            path: markdownlintRelativePath
        });
        return {
            id: "markdownlint-recommendation",
            status: "warning",
            message: "Recommended root .markdownlint.json is missing."
        };
    }
    try {
        const parsed = JSON.parse(node_fs_1.default.readFileSync(markdownlintPath, "utf8"));
        const isCompatible = parsed.MD025 !== undefined
            && typeof parsed.MD025 === "object"
            && parsed.MD025 !== null
            && parsed.MD025.front_matter_title === default_assets_1.RECOMMENDED_MARKDOWNLINT_CONTENTS.MD025.front_matter_title;
        if (!isCompatible) {
            findings.push({
                code: "MARKDOWNLINT_INCOMPATIBLE",
                message: `Root .markdownlint.json is present but incompatible with Day Shift Markdown conventions (expected MD025.front_matter_title = ${JSON.stringify(default_assets_1.RECOMMENDED_MARKDOWNLINT_CONTENTS.MD025.front_matter_title)}).`,
                severity: "warning",
                path: markdownlintRelativePath
            });
            return {
                id: "markdownlint-recommendation",
                status: "warning",
                message: "Root .markdownlint.json is incompatible with Day Shift conventions."
            };
        }
        return {
            id: "markdownlint-recommendation",
            status: "ok",
            message: "Root .markdownlint.json is present and compatible."
        };
    }
    catch {
        findings.push({
            code: "MARKDOWNLINT_INCOMPATIBLE",
            message: "Root .markdownlint.json is unreadable or invalid JSON.",
            severity: "warning",
            path: markdownlintRelativePath
        });
        return {
            id: "markdownlint-recommendation",
            status: "warning",
            message: "Root .markdownlint.json could not be parsed."
        };
    }
}
function runSandboxParentPathIdentityCheck(repositoryRoot, findings) {
    const sandboxParentPath = node_path_1.default.join(repositoryRoot, sandbox_contract_1.SANDBOX_PARENT_DIRECTORY);
    if (!node_fs_1.default.existsSync(sandboxParentPath)) {
        return {
            check: {
                id: "sandbox-parent-path-identity",
                status: "ok",
                message: "Sandbox parent path is not present yet; no disposable sandbox runs were detected."
            },
            sandboxParentPath: null
        };
    }
    let parentStat;
    try {
        parentStat = node_fs_1.default.lstatSync(sandboxParentPath);
    }
    catch {
        findings.push({
            code: "SANDBOX_PARENT_PATH_UNREADABLE",
            message: "Sandbox parent path exists but could not be inspected.",
            severity: "error",
            path: sandbox_contract_1.SANDBOX_PARENT_DIRECTORY
        });
        return {
            check: {
                id: "sandbox-parent-path-identity",
                status: "error",
                message: "Sandbox parent path could not be inspected."
            },
            sandboxParentPath: null
        };
    }
    if (parentStat.isSymbolicLink()) {
        findings.push({
            code: "SANDBOX_PARENT_PATH_INVALID",
            message: "Sandbox parent path must be a repository-local directory and must not be a symbolic link.",
            severity: "error",
            path: sandbox_contract_1.SANDBOX_PARENT_DIRECTORY
        });
        return {
            check: {
                id: "sandbox-parent-path-identity",
                status: "error",
                message: "Sandbox parent path is a symbolic link."
            },
            sandboxParentPath: null
        };
    }
    if (!parentStat.isDirectory()) {
        findings.push({
            code: "SANDBOX_PARENT_PATH_INVALID",
            message: "Sandbox parent path must be a directory.",
            severity: "error",
            path: sandbox_contract_1.SANDBOX_PARENT_DIRECTORY
        });
        return {
            check: {
                id: "sandbox-parent-path-identity",
                status: "error",
                message: "Sandbox parent path is not a directory."
            },
            sandboxParentPath: null
        };
    }
    try {
        const resolvedSandboxParentPath = node_fs_1.default.realpathSync(sandboxParentPath);
        if (!isWithinRepositoryRoot(repositoryRoot, resolvedSandboxParentPath)) {
            findings.push({
                code: "SANDBOX_PARENT_PATH_INVALID",
                message: "Sandbox parent path resolves outside the repository root.",
                severity: "error",
                path: sandbox_contract_1.SANDBOX_PARENT_DIRECTORY
            });
            return {
                check: {
                    id: "sandbox-parent-path-identity",
                    status: "error",
                    message: "Sandbox parent path resolves outside the repository root."
                },
                sandboxParentPath: null
            };
        }
    }
    catch {
        findings.push({
            code: "SANDBOX_PARENT_PATH_UNREADABLE",
            message: "Sandbox parent path exists but could not be resolved.",
            severity: "error",
            path: sandbox_contract_1.SANDBOX_PARENT_DIRECTORY
        });
        return {
            check: {
                id: "sandbox-parent-path-identity",
                status: "error",
                message: "Sandbox parent path could not be resolved."
            },
            sandboxParentPath: null
        };
    }
    return {
        check: {
            id: "sandbox-parent-path-identity",
            status: "ok",
            message: "Sandbox parent path resolves to a repository-local directory."
        },
        sandboxParentPath
    };
}
function runSandboxPathHygieneCheck(sandboxParentPath, findings) {
    if (sandboxParentPath === null) {
        return {
            id: "sandbox-path-hygiene",
            status: "ok",
            message: "Sandbox path hygiene is healthy; no sandbox parent path is present."
        };
    }
    const sandboxEntries = node_fs_1.default
        .readdirSync(sandboxParentPath, { withFileTypes: true })
        .sort(compareDirectoryEntryNames);
    let hasHygieneViolation = false;
    for (const entry of sandboxEntries) {
        const entryRelativePath = node_path_1.default.posix.join(sandbox_contract_1.SANDBOX_PARENT_DIRECTORY, entry.name);
        const entryAbsolutePath = node_path_1.default.join(sandboxParentPath, entry.name);
        if (!entry.isDirectory()) {
            hasHygieneViolation = true;
            findings.push({
                code: "SANDBOX_HYGIENE_NON_DIRECTORY_ENTRY",
                message: `Unexpected sandbox root entry '${entryRelativePath}' is outside the expected '${sandbox_contract_1.SANDBOX_PARENT_DIRECTORY}/${sandbox_contract_1.SANDBOX_RUN_DIRECTORY_PREFIX}<run-id>/' layout.`,
                severity: "error",
                path: entryRelativePath,
                recovery: createSandboxRootEntryRecovery(entryRelativePath)
            });
            continue;
        }
        if (!(0, sandbox_contract_1.isSandboxRunDirectoryName)(entry.name)) {
            hasHygieneViolation = true;
            findings.push({
                code: "SANDBOX_HYGIENE_UNEXPECTED_SIBLING_ROOT",
                message: `Unexpected sandbox root directory '${entryRelativePath}' is outside the expected '${sandbox_contract_1.SANDBOX_PARENT_DIRECTORY}/${sandbox_contract_1.SANDBOX_RUN_DIRECTORY_PREFIX}<run-id>/' layout.`,
                severity: "error",
                path: entryRelativePath,
                recovery: createSandboxRootEntryRecovery(entryRelativePath)
            });
            continue;
        }
        if (isNestedPlanningRootPresent(entryAbsolutePath)) {
            hasHygieneViolation = true;
            const nestedPlanningRelativePath = node_path_1.default.posix.join(entryRelativePath, ".day-shift", "planning");
            findings.push({
                code: "SANDBOX_HYGIENE_NESTED_PLANNING_ROOT",
                message: `Sandbox run content '${nestedPlanningRelativePath}' is a planning root; the expected authoritative location is repository '.day-shift/planning/'.`,
                severity: "error",
                path: nestedPlanningRelativePath,
                recovery: createNestedPlanningRootRecovery(nestedPlanningRelativePath)
            });
        }
    }
    if (hasHygieneViolation) {
        return {
            id: "sandbox-path-hygiene",
            status: "error",
            message: "Sandbox path hygiene violations were detected."
        };
    }
    return {
        id: "sandbox-path-hygiene",
        status: "ok",
        message: "Sandbox entries are owned run directories and satisfy path hygiene rules."
    };
}
function compareDirectoryEntryNames(left, right) {
    if (left.name < right.name)
        return -1;
    if (left.name > right.name)
        return 1;
    return 0;
}
function createSandboxRootEntryRecovery(entryRelativePath) {
    const expectedLocation = `${sandbox_contract_1.SANDBOX_PARENT_DIRECTORY}/${sandbox_contract_1.SANDBOX_RUN_DIRECTORY_PREFIX}<run-id>/`;
    return [
        `Relocate: move '${entryRelativePath}' beneath an owned '${expectedLocation}' directory when it is run evidence.`,
        `Retain: leave '${entryRelativePath}' unchanged while its purpose is investigated; doctor will continue to report it and will not modify it.`,
        `Dispose: remove '${entryRelativePath}' only after an operator confirms that the evidence is no longer needed.`
    ];
}
function createNestedPlanningRootRecovery(nestedPlanningRelativePath) {
    return [
        `Relocate: move authoritative planning content from '${nestedPlanningRelativePath}' to repository '.day-shift/planning/' through an explicit operator-controlled workflow.`,
        `Retain: leave '${nestedPlanningRelativePath}' unchanged while its provenance is investigated; doctor will continue to report it and will not modify it.`,
        `Dispose: remove '${nestedPlanningRelativePath}' only after an operator confirms that it is redundant and no evidence will be lost.`
    ];
}
function runSandboxCleanupReadinessCheck(repositoryRoot, sandboxParentPath, findings) {
    const legacyRuns = discoverLegacyLifecycleRuns(repositoryRoot);
    for (const runDirectory of legacyRuns) {
        findings.push({
            code: "LIFECYCLE_RUN_LEGACY_LOCATION",
            message: `Day Shift lifecycle run '${runDirectory}' is outside the managed '${sandbox_contract_1.SANDBOX_PARENT_DIRECTORY}/' tree.`,
            severity: "warning",
            path: runDirectory,
            recovery: [
                `Preview: day-shift resume run migrate --run-directory ${runDirectory} --dry-run --format json`,
                `Continue from the emitted '${sandbox_contract_1.SANDBOX_PARENT_DIRECTORY}/run-<run-id>' destination after applying the preview-bound migration.`,
                "Remove the legacy source only after the migrated run has been verified and explicit cleanup is intended."
            ]
        });
    }
    if (sandboxParentPath === null) {
        return {
            id: "sandbox-cleanup-readiness",
            status: legacyRuns.length > 0 ? "warning" : "ok",
            message: legacyRuns.length > 0
                ? `Found ${legacyRuns.length} legacy root lifecycle run director${legacyRuns.length === 1 ? "y" : "ies"} requiring migration.`
                : "No managed lifecycle runs were detected for cleanup guidance."
        };
    }
    const sandboxEntries = node_fs_1.default.readdirSync(sandboxParentPath, { withFileTypes: true });
    let staleRunCount = 0;
    for (const entry of sandboxEntries) {
        if (!entry.isDirectory() || !(0, sandbox_contract_1.isSandboxRunDirectoryName)(entry.name)) {
            continue;
        }
        const runAbsolutePath = node_path_1.default.join(sandboxParentPath, entry.name);
        const runRelativePath = (0, local_paths_1.toRepositoryRelativePath)(repositoryRoot, runAbsolutePath);
        const runEntries = node_fs_1.default.readdirSync(runAbsolutePath, { withFileTypes: true });
        if (runEntries.length === 0) {
            staleRunCount += 1;
            findings.push({
                code: "SANDBOX_STALE_RUN_CLEANUP_RECOMMENDED",
                message: "Empty sandbox run directory appears stale and can be cleaned up manually if no longer needed.",
                severity: "informational",
                path: runRelativePath
            });
        }
    }
    if (staleRunCount > 0) {
        return {
            id: "sandbox-cleanup-readiness",
            status: "informational",
            message: `Found ${staleRunCount} empty sandbox run director${staleRunCount === 1 ? "y" : "ies"} that may be safe to clean up.`
        };
    }
    if (legacyRuns.length > 0) {
        return {
            id: "sandbox-cleanup-readiness",
            status: "warning",
            message: `Found ${legacyRuns.length} legacy root lifecycle run director${legacyRuns.length === 1 ? "y" : "ies"} requiring migration.`
        };
    }
    return {
        id: "sandbox-cleanup-readiness",
        status: "ok",
        message: "No empty sandbox run directories were detected."
    };
}
function discoverLegacyLifecycleRuns(repositoryRoot) {
    const legacyParent = node_path_1.default.join(repositoryRoot, sandbox_contract_1.LEGACY_SANDBOX_PARENT_DIRECTORY);
    let entries;
    try {
        entries = node_fs_1.default.readdirSync(legacyParent, { withFileTypes: true });
    }
    catch {
        return [];
    }
    const results = [];
    for (const entry of entries
        .filter((candidate) => candidate.isDirectory() && (0, sandbox_contract_1.isSandboxRunDirectoryName)(candidate.name))
        .sort(compareDirectoryEntryNames)
        .slice(0, 512)) {
        const runDirectory = node_path_1.default.posix.join(sandbox_contract_1.LEGACY_SANDBOX_PARENT_DIRECTORY, entry.name);
        const manifestPath = node_path_1.default.join(legacyParent, entry.name, "run-context.json");
        try {
            const stat = node_fs_1.default.lstatSync(manifestPath);
            const parsed = JSON.parse(node_fs_1.default.readFileSync(manifestPath, "utf8"));
            if (!stat.isSymbolicLink() && stat.isFile() && parsed.runDirectory === runDirectory) {
                results.push(runDirectory);
            }
        }
        catch {
            // Arbitrary project-owned sandbox content is outside the Day Shift migration boundary.
        }
    }
    return results;
}
function isNestedPlanningRootPresent(runDirectoryPath) {
    const nestedPlanningRootPath = node_path_1.default.join(runDirectoryPath, ".day-shift", "planning");
    return (0, local_paths_1.isExistingDirectory)(nestedPlanningRootPath);
}
function isWithinRepositoryRoot(repositoryRoot, absolutePath) {
    const relativePath = node_path_1.default.relative(repositoryRoot, absolutePath);
    return relativePath === "" || (!relativePath.startsWith("..") && !node_path_1.default.isAbsolute(relativePath));
}
