/**
 * PlanArtifact v1 JSON Schema validation + draft normalization (WP-3.2 / T100456).
 */
import { randomUUID } from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import Ajv2020Import from "ajv/dist/2020.js";
import { PLAN_ARTIFACT_SCHEMA_VERSION } from "./plan-artifact-v1.js";
import { validatePlanArtifactWbsItemShape } from "./normalize-wbs-to-task-draft.js";
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ajv/dist/2020 default export shape varies by bundler
const Ajv2020Ctor = Ajv2020Import.default ?? Ajv2020Import;
const PLAN_SCHEMA_ID = "https://workflow-cannon.dev/schemas/planning/plan-artifact.v1.json";
let cachedValidator = null;
let cachedValidatorRoot = null;
/** Prefer workspace when it ships schemas; else package root (dist/core/planning → repo/package). */
export function resolvePlanArtifactSchemaRoot(workspacePath) {
    const cwd = workspacePath ? path.resolve(workspacePath) : process.cwd();
    const inWorkspace = path.join(cwd, "schemas", "planning", "plan-artifact.v1.schema.json");
    if (fs.existsSync(inWorkspace)) {
        return cwd;
    }
    return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
}
function loadPlanArtifactValidator(workspaceRoot) {
    const root = resolvePlanArtifactSchemaRoot(workspaceRoot);
    if (cachedValidator && cachedValidatorRoot === root) {
        return cachedValidator;
    }
    const schemasDir = path.join(root, "schemas", "planning");
    const ajv = new Ajv2020Ctor({ strict: true, allErrors: true });
    const wbsSchema = JSON.parse(fs.readFileSync(path.join(schemasDir, "plan-artifact-wbs-item.v1.schema.json"), "utf8"));
    const planSchema = JSON.parse(fs.readFileSync(path.join(schemasDir, "plan-artifact.v1.schema.json"), "utf8"));
    ajv.addSchema(wbsSchema);
    ajv.addSchema(planSchema);
    const validate = ajv.getSchema(PLAN_SCHEMA_ID);
    if (!validate) {
        throw new Error("plan-artifact.v1 schema failed to register");
    }
    cachedValidator = validate;
    cachedValidatorRoot = root;
    return validate;
}
export function formatPlanArtifactInstancePath(instancePath) {
    if (!instancePath || instancePath.length === 0) {
        return "(root)";
    }
    return instancePath.replace(/^\//, "").replace(/\//g, ".");
}
function ajvErrorsToPlanArtifactErrors(errors) {
    if (!errors?.length) {
        return [{ path: "(root)", message: "validation failed" }];
    }
    return errors.map((e) => ({
        path: formatPlanArtifactInstancePath(e.instancePath),
        message: e.message ?? e.keyword ?? "validation error",
        keyword: e.keyword
    }));
}
function asRecord(value) {
    if (!value || typeof value !== "object" || Array.isArray(value)) {
        return null;
    }
    return value;
}
function cloneJson(value) {
    return JSON.parse(JSON.stringify(value));
}
function expectedPlanRef(planId) {
    return `plan-artifact:${planId}`;
}
/**
 * Fill envelope defaults for draft argv without overwriting supplied content.
 */
export function normalizePlanArtifactDraft(raw, options = {}) {
    const base = asRecord(raw);
    if (!base) {
        return { schemaVersion: PLAN_ARTIFACT_SCHEMA_VERSION };
    }
    const doc = cloneJson(base);
    doc.schemaVersion = PLAN_ARTIFACT_SCHEMA_VERSION;
    const planId = (typeof options.planId === "string" && options.planId.trim()) ||
        (typeof doc.planId === "string" && doc.planId.trim()) ||
        randomUUID();
    doc.planId = planId;
    if (typeof doc.version !== "number" || !Number.isInteger(doc.version) || doc.version < 1) {
        doc.version = 1;
    }
    const planRef = expectedPlanRef(planId);
    doc.planRef = planRef;
    if (typeof doc.status !== "string" || !doc.status.trim()) {
        doc.status = "draft";
    }
    const now = options.now ?? new Date().toISOString();
    const actor = options.actor?.trim() || "agent";
    const provenance = asRecord(doc.provenance) ?? {};
    let source = "draft-plan-artifact";
    if (options.importSource) {
        source = options.importSource;
    }
    else {
        const existingSource = provenance.source;
        if (existingSource === "import-build-plan" || existingSource === "import-wishlist") {
            source = existingSource;
        }
    }
    provenance.createdAt = typeof provenance.createdAt === "string" ? provenance.createdAt : now;
    provenance.updatedAt = typeof provenance.updatedAt === "string" ? provenance.updatedAt : now;
    provenance.createdBy = typeof provenance.createdBy === "string" ? provenance.createdBy : actor;
    provenance.source = source;
    doc.provenance = provenance;
    if (!Array.isArray(doc.nonGoals)) {
        doc.nonGoals = [];
    }
    if (!Array.isArray(doc.riskAssessment)) {
        doc.riskAssessment = [];
    }
    if (!Array.isArray(doc.assumptions)) {
        doc.assumptions = [];
    }
    if (!Array.isArray(doc.openQuestions)) {
        doc.openQuestions = [];
    }
    if (!Array.isArray(doc.wbs)) {
        doc.wbs = [];
    }
    return doc;
}
function validatePlanRefConsistency(doc) {
    const errors = [];
    if (typeof doc.planId === "string" && typeof doc.planRef === "string") {
        const expected = expectedPlanRef(doc.planId);
        if (doc.planRef !== expected) {
            errors.push({
                path: "planRef",
                message: `planRef must be ${expected}`,
                keyword: "planRefMismatch"
            });
        }
    }
    return errors;
}
function validateWbsShapeRows(doc) {
    const errors = [];
    const wbs = doc.wbs;
    if (!Array.isArray(wbs)) {
        return errors;
    }
    wbs.forEach((row, index) => {
        const guarded = validatePlanArtifactWbsItemShape(row);
        if (guarded.ok) {
            return;
        }
        for (const finding of guarded.findings) {
            const suffix = finding.field ? `.${finding.field}` : "";
            errors.push({
                path: `wbs[${index}]${suffix}`,
                message: finding.message,
                keyword: finding.code
            });
        }
    });
    return errors;
}
/**
 * Validate a PlanArtifact document (call {@link normalizePlanArtifactDraft} first for partial argv).
 */
export function validatePlanArtifactDocument(doc, options = {}) {
    const workspaceRoot = resolvePlanArtifactSchemaRoot(options.workspaceRoot);
    const validate = loadPlanArtifactValidator(workspaceRoot);
    const record = asRecord(doc);
    if (!record) {
        return {
            ok: false,
            code: "plan-artifact-schema-invalid",
            errors: [{ path: "(root)", message: "artifact must be a JSON object" }]
        };
    }
    const errors = [...validatePlanRefConsistency(record)];
    const schemaOk = validate(record);
    if (!schemaOk) {
        errors.push(...ajvErrorsToPlanArtifactErrors(validate.errors));
    }
    errors.push(...validateWbsShapeRows(record));
    if (errors.length > 0) {
        return { ok: false, code: "plan-artifact-schema-invalid", errors };
    }
    return { ok: true, artifact: record };
}
/**
 * Normalize argv `artifact`, then validate. Convenience for `draft-plan-artifact`.
 */
export function validatePlanArtifactDraftInput(raw, options = {}) {
    const normalized = normalizePlanArtifactDraft(raw, options);
    return validatePlanArtifactDocument(normalized, { workspaceRoot: options.workspaceRoot });
}
