"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateArtifactShape = validateArtifactShape;
exports.parseArtifactMarkdown = parseArtifactMarkdown;
const node_path_1 = __importDefault(require("node:path"));
const artifact_family_1 = require("./artifact-family");
const planning_artifact_document_1 = require("../planning/planning-artifact-document");
const semantic_placeholder_1 = require("../planning/semantic-placeholder");
const ordered_root_identity_1 = require("../planning/ordered-root-identity");
const reconciliation_resolution_1 = require("../planning/reconciliation-resolution");
const phase_overview_contract_1 = require("../planning/phase-overview-contract");
const specification_contract_1 = require("../specification-contract");
const STANDARD_TASK_REQUIRED_H2 = [
    "Objective",
    "Upstream Context",
    "Implementation Target Paths",
    "Current Behavior",
    "Desired Behavior",
    "Runtime Delta",
    "Acceptance Criteria",
    "Validation Commands",
    "Rollback / Recovery",
    "Risks",
    "Dependencies",
    "Constraints",
    "Assumptions",
    "Lifecycle / Closure Expectations",
    "Open Questions"
];
const BASIC_TASK_REQUIRED_H2 = [
    "Upstream Context",
    "Objective",
    ...STANDARD_TASK_REQUIRED_H2.slice(2)
];
const REQUIRED_H2_BY_ARTIFACT_TYPE = new Map([
    [
        "specification",
        specification_contract_1.CANONICAL_SPECIFICATION_H2_ORDER
    ],
    [
        "slice_overview",
        [
            "Purpose",
            "Source Specification",
            "Desired Outcome",
            "Scope",
            "Non-Goals",
            "Planning Depth",
            "Phases",
            "Decisions",
            "Risks",
            "Validation Strategy",
            "Open Questions"
        ]
    ],
    [
        "phase_overview",
        [
            "Purpose",
            "Scope",
            "Milestones",
            "Acceptance Criteria",
            "Dependencies",
            "Risks",
            "Validation Focus",
            "Open Questions"
        ]
    ],
    [
        "milestone_overview",
        [
            "Purpose",
            "Scope",
            "Tasks",
            "Acceptance Criteria",
            "Dependencies",
            "Risks",
            "Validation Focus",
            "Open Questions"
        ]
    ],
    [
        "task_definition",
        STANDARD_TASK_REQUIRED_H2
    ],
    [
        "implementation_summary",
        [
            "Summary",
            "Changed Paths",
            "Acceptance Criteria Results",
            "Validation Performed",
            "Scope Classification",
            "Deviations",
            "Risks and Follow-Ups",
            "Open Questions"
        ]
    ],
    [
        "reconciliation",
        reconciliation_resolution_1.MILESTONE_RECONCILIATION_SECTION_ORDER
    ]
]);
const TEMPLATE_H2_BY_PATH = new Map([
    [
        ".day-shift/templates/specification.md",
        specification_contract_1.CANONICAL_SPECIFICATION_H2_ORDER
    ],
    [
        ".day-shift/templates/phase-overview.md",
        phase_overview_contract_1.PHASE_OVERVIEW_H2_ORDER
    ],
    [
        ".day-shift/templates/task-definition.md",
        REQUIRED_H2_BY_ARTIFACT_TYPE.get("task_definition") ?? []
    ],
    [
        ".day-shift/templates/implementation-summary.md",
        REQUIRED_H2_BY_ARTIFACT_TYPE.get("implementation_summary") ?? []
    ],
    [
        ".day-shift/templates/milestone-reconciliation.md",
        reconciliation_resolution_1.MILESTONE_RECONCILIATION_SECTION_ORDER
    ],
    [
        ".day-shift/templates/reconciliation.md",
        reconciliation_resolution_1.TASK_RECONCILIATION_SECTION_ORDER
    ]
]);
const SCAFFOLD_PROSE_PHRASES = [
    "Template notes:",
    "Use this section to",
    "List only",
    "Keep only",
    "If there are none, use exactly",
    "This implementation summary is initialized as the stable execution artifact for the task.",
    "This reconciliation artifact is initialized as a milestone-generation placeholder"
];
const PROMPT_REQUIRED_FRONT_MATTER_FIELDS = ["prompt_version", "layer", "action"];
const PROMPT_LAYER_VALUES = new Set([
    "specification",
    "slice",
    "phase",
    "milestone",
    "task",
    "implementation",
    "reconciliation"
]);
const PROMPT_ACTION_VALUES = new Set(["build", "next", "review"]);
const PROMPT_FRONT_MATTER_FIELD_SET = new Set(PROMPT_REQUIRED_FRONT_MATTER_FIELDS);
function validateArtifactShape(input) {
    const family = (0, artifact_family_1.detectArtifactFamily)(input);
    const findings = [];
    if (!family.supported) {
        findings.push(createFinding("UNSUPPORTED_ARTIFACT_FAMILY", input.path, family.reason));
        return {
            path: family.path,
            family,
            markdown: null,
            json: null,
            findings
        };
    }
    if (family.contentFormat === "json") {
        return validateJsonArtifact(input.path, input.content, family, findings);
    }
    if (family.contentFormat !== "markdown") {
        return {
            path: family.path,
            family,
            markdown: null,
            json: null,
            findings
        };
    }
    const markdown = parseArtifactMarkdown(input.content);
    if (family.metadataBearing && !markdown.hasFrontMatter) {
        findings.push(createFinding("MISSING_FRONT_MATTER", family.path, "Artifact is missing required YAML front matter."));
    }
    if (family.metadataBearing && markdown.malformedFrontMatter) {
        findings.push(createFinding("MALFORMED_FRONT_MATTER", family.path, "Artifact YAML front matter is not properly closed."));
    }
    if (markdown.hasFrontMatter && !markdown.malformedFrontMatter) {
        validateFamilySpecificFrontMatter(family, markdown.frontMatterText, findings);
        validateOrderedPlanningDirectoryShape(family.path, markdown.frontMatterText, findings);
    }
    validateVisibleHeadingShape(family, markdown.frontMatterText, markdown.bodyText, findings);
    if (!family.sourceTemplate && !family.scaffoldSource) {
        validatePlaceholderResidue(family.path, markdown.bodyText, findings);
        validateScaffoldProse(family.path, markdown.bodyText, findings);
    }
    return {
        path: family.path,
        family,
        markdown,
        json: null,
        findings
    };
}
function validateOrderedPlanningDirectoryShape(artifactPath, frontMatterText, findings) {
    const fields = readFlatFrontMatter(frontMatterText);
    const artifactType = fields.get("artifact_type");
    const workflowLevel = fields.get("workflow_level");
    const stableId = fields.get("id") ?? fields.get("task") ?? null;
    if (stableId === null)
        return;
    const basicMatch = artifactPath.match(/^\.day-shift\/planning\/basic\/tasks\/([^/]+)\/(?:task-definition|implementation-summary)\.md$/u);
    if (basicMatch !== null && workflowLevel === "basic") {
        validateRootDirectoryShape({
            context: "basic-root",
            artifactPath,
            basename: basicMatch[1],
            stableId,
            implementationOrder: fields.get("implementation_order")
        }, findings);
        return;
    }
    const overviewMatch = artifactPath.match(/^\.day-shift\/planning\/structured\/([^/]+)\/overview\.md$/u);
    if (overviewMatch !== null && artifactType === "work_overview" && workflowLevel === "structured") {
        validateRootDirectoryShape({
            context: "structured-root",
            artifactPath,
            basename: overviewMatch[1],
            stableId,
            implementationOrder: fields.get("implementation_order")
        }, findings);
        return;
    }
    const taskMatch = artifactPath.match(/^\.day-shift\/planning\/structured\/[^/]+\/tasks\/([^/]+)\/(task-definition|implementation-summary)\.md$/u);
    if (taskMatch === null || workflowLevel !== "structured")
        return;
    const identity = parseObservedDirectoryIdentity("structured-task", taskMatch[1], stableId, artifactPath, findings);
    if (identity.shape === "legacy") {
        findings.push(createFinding("LEGACY_ORDERED_DIRECTORY", artifactPath, `Structured task directory '${taskMatch[1]}' is readable compatibility input and remains a migration candidate.`, "informational"));
        return;
    }
    if (taskMatch[2] !== "task-definition")
        return;
    const sequenceValue = fields.get("sequence");
    if (sequenceValue === undefined || sequenceValue.trim() === "") {
        findings.push(createFinding("MISSING_TASK_SEQUENCE", artifactPath, "A numbered Structured task directory requires task-definition sequence metadata."));
        return;
    }
    if (!/^(?:[1-9]|[1-9]\d)$/u.test(sequenceValue)) {
        findings.push(createFinding("INVALID_TASK_SEQUENCE", artifactPath, "Structured task sequence must be an integer from 1 through 99."));
        return;
    }
    const expectedPrefix = String(Number.parseInt(sequenceValue, 10)).padStart(2, "0");
    if (identity.orderPrefix !== expectedPrefix) {
        findings.push(createFinding("TASK_SEQUENCE_PREFIX_MISMATCH", artifactPath, `Structured task directory prefix '${identity.orderPrefix}' does not match sequence '${sequenceValue}'.`));
    }
}
function validateRootDirectoryShape(input, findings) {
    const identity = parseObservedDirectoryIdentity(input.context, input.basename, input.stableId, input.artifactPath, findings);
    if (identity.shape === "legacy") {
        findings.push(createFinding("LEGACY_ORDERED_DIRECTORY", input.artifactPath, `${input.context} directory '${input.basename}' is readable compatibility input and remains a migration candidate.`, "informational"));
        return;
    }
    if (input.implementationOrder === undefined || input.implementationOrder.trim() === "") {
        findings.push(createFinding("MISSING_IMPLEMENTATION_ORDER", input.artifactPath, `Numbered ${input.context} directory '${input.basename}' requires implementation_order metadata.`));
        return;
    }
    let implementationOrder;
    try {
        implementationOrder = (0, ordered_root_identity_1.normalizeImplementationOrder)(input.implementationOrder);
    }
    catch (error) {
        findings.push(createFinding("INVALID_IMPLEMENTATION_ORDER", input.artifactPath, error instanceof Error ? error.message : "implementation_order is invalid."));
        return;
    }
    if (identity.orderPrefix !== implementationOrder) {
        findings.push(createFinding("ORDER_PREFIX_MISMATCH", input.artifactPath, `Directory prefix '${identity.orderPrefix}' does not match implementation_order '${implementationOrder}'.`));
    }
}
function parseObservedDirectoryIdentity(context, basename, stableId, artifactPath, findings) {
    if (basename === stableId) {
        return { context, basename, stableId, order: null, orderPrefix: null, shape: "legacy" };
    }
    try {
        const identity = (0, ordered_root_identity_1.parseOrderedDirectoryIdentity)(context, basename);
        if (identity.stableId !== stableId) {
            findings.push(createFinding("ORDERED_DIRECTORY_IDENTITY_MISMATCH", artifactPath, `Directory '${basename}' does not match stable identity '${stableId}'.`));
        }
        return identity;
    }
    catch (error) {
        findings.push(createFinding("ORDERED_DIRECTORY_IDENTITY_MISMATCH", artifactPath, error instanceof Error ? error.message : `Directory '${basename}' is invalid.`));
        return { context, basename: node_path_1.default.posix.basename(basename), stableId, order: null, orderPrefix: null, shape: "legacy" };
    }
}
function validateFamilySpecificFrontMatter(family, frontMatterText, findings) {
    const frontMatter = readFlatFrontMatter(frontMatterText);
    if (family.family === "prompt") {
        validatePromptFrontMatter(family.path, frontMatter, findings);
        return;
    }
    if (family.family === "template") {
        validateTemplateFrontMatter(family.path, frontMatter, findings);
    }
}
function validatePromptFrontMatter(artifactPath, frontMatter, findings) {
    for (const field of PROMPT_REQUIRED_FRONT_MATTER_FIELDS) {
        if (!frontMatter.has(field) || (frontMatter.get(field) ?? "").trim() === "") {
            findings.push(createFinding("MISSING_PROMPT_FRONT_MATTER_FIELD", artifactPath, `Prompt artifact is missing required front matter field '${field}'.`));
        }
    }
    const promptVersion = frontMatter.get("prompt_version");
    if (promptVersion !== undefined && !/^[1-9][0-9]*$/u.test(promptVersion)) {
        findings.push(createFinding("INVALID_PROMPT_FRONT_MATTER_FIELD", artifactPath, "Prompt front matter field 'prompt_version' must be a positive integer."));
    }
    const layer = frontMatter.get("layer");
    if (layer !== undefined && !PROMPT_LAYER_VALUES.has(layer)) {
        findings.push(createFinding("INVALID_PROMPT_FRONT_MATTER_FIELD", artifactPath, `Prompt front matter field 'layer' has unsupported value '${layer}'.`));
    }
    const action = frontMatter.get("action");
    if (action !== undefined && !PROMPT_ACTION_VALUES.has(action)) {
        findings.push(createFinding("INVALID_PROMPT_FRONT_MATTER_FIELD", artifactPath, `Prompt front matter field 'action' has unsupported value '${action}'.`));
    }
}
function validateTemplateFrontMatter(artifactPath, frontMatter, findings) {
    for (const field of PROMPT_FRONT_MATTER_FIELD_SET) {
        if (frontMatter.has(field)) {
            findings.push(createFinding("INVALID_TEMPLATE_FRONT_MATTER_FIELD", artifactPath, `Template artifact front matter must not use prompt-family field '${field}'.`));
        }
    }
}
function readFlatFrontMatter(frontMatterText) {
    const values = new Map();
    for (const line of frontMatterText.split(/\r?\n/)) {
        const match = line.match(/^([A-Za-z0-9_]+):\s*(.*)$/u);
        if (match === null) {
            continue;
        }
        values.set(match[1], unquoteScalar(match[2].trim()));
    }
    return values;
}
function unquoteScalar(value) {
    if ((value.startsWith("\"") && value.endsWith("\"")) ||
        (value.startsWith("'") && value.endsWith("'"))) {
        return value.slice(1, -1);
    }
    return value;
}
function parseArtifactMarkdown(markdownText) {
    const document = (0, planning_artifact_document_1.parsePlanningArtifactDocument)(markdownText);
    if (!document.hasFrontMatter) {
        const malformed = document.diagnostics.some((diagnostic) => diagnostic.code === "unterminated-front-matter");
        return {
            frontMatterText: malformed ? markdownText.split(/\r?\n/).slice(1).join("\n") : "",
            bodyText: markdownText,
            hasFrontMatter: false,
            malformedFrontMatter: malformed
        };
    }
    return {
        frontMatterText: document.frontMatterLines.join("\n"),
        bodyText: document.bodyText.split(/\r?\n/).join("\n"),
        hasFrontMatter: true,
        malformedFrontMatter: false
    };
}
function validateJsonArtifact(artifactPath, content, family, findings) {
    try {
        const json = JSON.parse(content);
        validateManifestJsonShape(family, json, findings);
        return {
            path: family.path,
            family,
            markdown: null,
            json,
            findings
        };
    }
    catch {
        findings.push(createFinding("MALFORMED_JSON_MANIFEST", artifactPath, "JSON manifest content is malformed and was not parsed."));
        return {
            path: family.path,
            family,
            markdown: null,
            json: null,
            findings
        };
    }
}
function validateManifestJsonShape(family, json, findings) {
    if (!isPlainObject(json)) {
        findings.push(createFinding("MALFORMED_JSON_MANIFEST", family.path, "JSON manifest content must be an object."));
        return;
    }
    if (family.family === "prompt-manifest") {
        validatePromptManifestJson(family.path, json, findings);
        return;
    }
    if (family.family === "template-manifest") {
        validateTemplateManifestJson(family.path, json, findings);
    }
}
function validatePromptManifestJson(artifactPath, manifest, findings) {
    if (typeof manifest.prompt_manifest_version !== "number") {
        findings.push(createFinding("MISSING_PROMPT_MANIFEST_FIELD", artifactPath, "Prompt manifest is missing numeric 'prompt_manifest_version'.", "warning"));
    }
    if (typeof manifest.prompt_version !== "number") {
        findings.push(createFinding("MISSING_PROMPT_MANIFEST_FIELD", artifactPath, "Prompt manifest is missing numeric top-level 'prompt_version'.", "warning"));
    }
    if (!Array.isArray(manifest.prompts)) {
        findings.push(createFinding("MISSING_PROMPT_MANIFEST_FIELD", artifactPath, "Prompt manifest is missing required 'prompts' array."));
        return;
    }
    manifest.prompts.forEach((entry, index) => {
        const entryPath = isPlainObject(entry) && typeof entry.path === "string"
            ? entry.path
            : `${artifactPath}#prompts[${index}]`;
        if (!isPlainObject(entry)) {
            findings.push(createFinding("INVALID_PROMPT_MANIFEST_FIELD", entryPath, "Prompt manifest entry must be an object."));
            return;
        }
        for (const field of ["path", "layer", "action"]) {
            if (typeof entry[field] !== "string" || entry[field] === "") {
                findings.push(createFinding("MISSING_PROMPT_MANIFEST_FIELD", entryPath, `Prompt manifest entry is missing string '${field}'.`, "warning"));
            }
        }
        if (typeof entry.version !== "number") {
            findings.push(createFinding("MISSING_PROMPT_MANIFEST_FIELD", entryPath, "Prompt manifest entry is missing numeric 'version'.", "warning"));
        }
        if (entry.template_ref !== undefined && typeof entry.template_ref !== "string") {
            findings.push(createFinding("INVALID_PROMPT_MANIFEST_FIELD", entryPath, "Prompt manifest entry field 'template_ref' must be a string."));
        }
        if (entry.template_refs !== undefined && !isStringArray(entry.template_refs)) {
            findings.push(createFinding("INVALID_PROMPT_MANIFEST_FIELD", entryPath, "Prompt manifest entry field 'template_refs' must be an array of strings."));
        }
        if (entry.template_ref !== undefined && entry.template_refs !== undefined) {
            findings.push(createFinding("INVALID_PROMPT_MANIFEST_FIELD", entryPath, "Prompt manifest entry must preserve either 'template_ref' or 'template_refs', not both."));
        }
        if (entry.destination !== undefined) {
            findings.push(createFinding("INVALID_PROMPT_MANIFEST_FIELD", entryPath, "Prompt manifest entries must not declare template-manifest destination routing."));
        }
    });
}
function validateTemplateManifestJson(artifactPath, manifest, findings) {
    if (typeof manifest.template_manifest_version !== "number") {
        findings.push(createFinding("MISSING_TEMPLATE_MANIFEST_FIELD", artifactPath, "Template manifest is missing numeric 'template_manifest_version'.", "warning"));
    }
    if (typeof manifest.template_version !== "number") {
        findings.push(createFinding("MISSING_TEMPLATE_MANIFEST_FIELD", artifactPath, "Template manifest is missing numeric top-level 'template_version'.", "warning"));
    }
    if (!Array.isArray(manifest.templates)) {
        findings.push(createFinding("MISSING_TEMPLATE_MANIFEST_FIELD", artifactPath, "Template manifest is missing required 'templates' array."));
        return;
    }
    manifest.templates.forEach((entry, index) => {
        const entryPath = isPlainObject(entry) && typeof entry.path === "string"
            ? entry.path
            : `${artifactPath}#templates[${index}]`;
        if (!isPlainObject(entry)) {
            findings.push(createFinding("INVALID_TEMPLATE_MANIFEST_FIELD", entryPath, "Template manifest entry must be an object."));
            return;
        }
        for (const field of ["path", "kind", "artifact_type"]) {
            if (typeof entry[field] !== "string" || entry[field] === "") {
                findings.push(createFinding("MISSING_TEMPLATE_MANIFEST_FIELD", entryPath, `Template manifest entry is missing string '${field}'.`, "warning"));
            }
        }
        if (typeof entry.version !== "number") {
            findings.push(createFinding("MISSING_TEMPLATE_MANIFEST_FIELD", entryPath, "Template manifest entry is missing numeric 'version'.", "warning"));
        }
        if (entry.template_ref !== undefined || entry.template_refs !== undefined) {
            findings.push(createFinding("INVALID_TEMPLATE_MANIFEST_FIELD", entryPath, "Template manifest entries must not use prompt-family template_ref or template_refs fields."));
        }
        if (entry.destination !== undefined) {
            if (typeof entry.destination !== "string" || entry.destination === "") {
                findings.push(createFinding("INVALID_SCAFFOLD_DESTINATION_FIELD", entryPath, "Template manifest entry field 'destination' must be a non-empty string."));
            }
            if (entry.kind !== "scaffold") {
                findings.push(createFinding("INVALID_SCAFFOLD_DESTINATION_FIELD", entryPath, "Template manifest destination routing is allowed only for scaffold entries."));
            }
        }
    });
}
function validateVisibleHeadingShape(family, frontMatterText, bodyText, findings) {
    const h1Headings = collectHeadings(bodyText, 1);
    if (h1Headings.length === 0) {
        findings.push(createFinding("MISSING_H1", family.path, "Artifact body must include exactly one visible H1 heading."));
    }
    if (h1Headings.length > 1) {
        findings.push(createFinding("MULTIPLE_H1", family.path, "Artifact body must not include more than one visible H1 heading."));
    }
    const requiredH2 = getRequiredH2Contract(family, frontMatterText);
    if (requiredH2.length === 0) {
        return;
    }
    const observedH2 = collectHeadings(bodyText, 2);
    let lastObservedIndex = -1;
    for (const expected of requiredH2) {
        const matchingIndexes = observedH2.flatMap((heading, index) => heading === expected ? [index] : []);
        const index = matchingIndexes[0] ?? -1;
        if (index < 0) {
            findings.push(createFinding("MISSING_REQUIRED_H2", family.path, `Artifact is missing required H2 heading '${expected}'.`));
            continue;
        }
        if (matchingIndexes.length > 1) {
            findings.push(createFinding("DUPLICATE_REQUIRED_H2", family.path, `Artifact contains required H2 heading '${expected}' ${matchingIndexes.length} times; exactly one is required.`));
        }
        if (index < lastObservedIndex) {
            findings.push(createFinding("OUT_OF_ORDER_REQUIRED_H2", family.path, `Required H2 heading '${expected}' is out of order.`));
            continue;
        }
        lastObservedIndex = index;
    }
}
function validatePlaceholderResidue(artifactPath, bodyText, findings) {
    const [placeholder] = (0, semantic_placeholder_1.findSemanticPlaceholderContent)(bodyText);
    if (placeholder !== undefined) {
        findings.push(createFinding("UNRESOLVED_PLACEHOLDER", artifactPath, `Artifact body contains unresolved ${placeholder.kind} text on authored line ${placeholder.line}.`));
    }
}
function validateScaffoldProse(artifactPath, bodyText, findings) {
    for (const phrase of SCAFFOLD_PROSE_PHRASES) {
        if (bodyText.includes(phrase)) {
            findings.push(createFinding("SCAFFOLD_PROSE_LEAK", artifactPath, `Artifact body contains scaffold instruction prose '${phrase}'.`));
            return;
        }
    }
}
function getRequiredH2Contract(family, frontMatterText) {
    if (family.artifactType === "task_definition"
        && readFlatFrontMatter(frontMatterText).get("workflow_level") === "basic") {
        return BASIC_TASK_REQUIRED_H2;
    }
    if (family.artifactType === "reconciliation") {
        return readFlatFrontMatter(frontMatterText).has("task")
            ? reconciliation_resolution_1.TASK_RECONCILIATION_SECTION_ORDER
            : reconciliation_resolution_1.MILESTONE_RECONCILIATION_SECTION_ORDER;
    }
    if (family.artifactType !== null) {
        return REQUIRED_H2_BY_ARTIFACT_TYPE.get(family.artifactType) ?? [];
    }
    if (family.sourceTemplate) {
        return TEMPLATE_H2_BY_PATH.get(family.path) ?? [];
    }
    return [];
}
function collectHeadings(markdownText, level) {
    const prefix = "#".repeat(level);
    const pattern = new RegExp(`^${prefix} (.+)$`);
    const headings = [];
    for (const line of markdownText.split(/\r?\n/)) {
        const match = line.match(pattern);
        if (match !== null) {
            headings.push(match[1].trim());
        }
    }
    return headings;
}
function createFinding(code, artifactPath, message, severity = "error") {
    return {
        code,
        severity,
        path: artifactPath.replace(/\\/g, "/").replace(/^\.\//, ""),
        message
    };
}
function isPlainObject(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
function isStringArray(value) {
    return Array.isArray(value) && value.every((entry) => typeof entry === "string");
}
