"use client";
import { useMemo, useRef, useState, useEffect } from "react";
import { parseDocument, isMap, isSeq, isScalar } from "yaml";
import { TASK_NAME_PATTERN, TASK_NAME_PATTERN_ERROR } from "./canvas-constants.js";
const DEBOUNCE_MS = 150;
const VALID_TASK_KINDS = new Set([
    "set_vars", "http_call", "grpc_call", "activity_call", "switch_case",
    "for_each", "fork", "try_catch", "listen", "wait", "raise_error",
    "run_workflow", "agent_call", "llm_call", "transform", "human_input",
    "validate", "emit_event", "notification", "eval",
]);
/**
 * Behavior hook that validates workflow YAML and produces CodeMirror diagnostics.
 *
 * The pipeline runs entirely client-side with 150ms debounce:
 * 1. YAML syntax check (parse errors)
 * 2. Structural validation (required fields: document, tasks)
 * 3. Per-task kind validation (known kinds, required task fields)
 * 4. Task config schema validation (against TaskKindRegistry JSON schemas)
 * 5. Reference validation (flow.then targets, duplicate task names)
 *
 * Each diagnostic is source-mapped to exact character positions using the
 * `yaml` library's CST range information.
 *
 * @param yaml - The current YAML content string.
 * @param registry - The task kind registry for schema validation.
 *
 * @since T10 (YAML Editor with Graph Preview)
 */
export function useWorkflowValidation(yaml, registry) {
    const [diagnostics, setDiagnostics] = useState([]);
    const timerRef = useRef(null);
    const registryRef = useRef(registry);
    registryRef.current = registry;
    useEffect(() => {
        if (timerRef.current)
            clearTimeout(timerRef.current);
        timerRef.current = setTimeout(() => {
            const result = validateWorkflowYaml(yaml, registryRef.current);
            setDiagnostics(result);
        }, DEBOUNCE_MS);
        return () => {
            if (timerRef.current)
                clearTimeout(timerRef.current);
        };
    }, [yaml]);
    return useMemo(() => ({ diagnostics }), [diagnostics]);
}
// ---------------------------------------------------------------------------
// Core validation pipeline
// ---------------------------------------------------------------------------
function validateWorkflowYaml(yaml, registry) {
    if (!yaml.trim())
        return [];
    const diags = [];
    let doc;
    try {
        doc = parseDocument(yaml, { keepSourceTokens: true });
    }
    catch {
        diags.push({ from: 0, to: Math.min(yaml.length, 1), severity: "error", message: "Invalid YAML syntax" });
        return diags;
    }
    for (const err of doc.errors) {
        const [from, to] = err.pos ?? [0, 1];
        diags.push({ from, to: Math.max(to, from + 1), severity: "error", message: err.message });
    }
    if (diags.length > 0)
        return diags;
    const root = doc.contents;
    if (!isMap(root)) {
        diags.push({ from: 0, to: Math.min(yaml.length, 1), severity: "error", message: "Workflow YAML must be a mapping document" });
        return diags;
    }
    validateStructure(root, diags, yaml);
    validateTasks(root, diags, yaml, registry);
    return diags;
}
// ---------------------------------------------------------------------------
// Structural validation
// ---------------------------------------------------------------------------
function validateStructure(root, diags, yaml) {
    const specNode = findMapValue(root, "spec");
    if (!specNode || !isMap(specNode)) {
        diags.push(diagAtKey(root, "spec", yaml, "error", "Missing required field: spec"));
        return;
    }
    const docNode = findMapValue(specNode, "document");
    if (!docNode || !isMap(docNode)) {
        diags.push(diagAtKey(specNode, "document", yaml, "error", "Missing required field: spec.document"));
    }
    else {
        for (const field of ["namespace", "name", "version"]) {
            const val = findScalarValue(docNode, field);
            if (!val) {
                diags.push(diagAtKey(docNode, field, yaml, "error", `Missing required field: spec.document.${field}`));
            }
        }
    }
    const tasksNode = findMapValue(specNode, "tasks");
    if (!tasksNode || !isSeq(tasksNode) || tasksNode.items.length === 0) {
        diags.push(diagAtKey(specNode, "tasks", yaml, "error", "Missing required field: spec.tasks (must be a non-empty list)"));
    }
}
// ---------------------------------------------------------------------------
// Task-level validation
// ---------------------------------------------------------------------------
function validateTasks(root, diags, yaml, registry) {
    const specNode = findMapValue(root, "spec");
    if (!specNode || !isMap(specNode))
        return;
    const tasksNode = findMapValue(specNode, "tasks");
    if (!tasksNode || !isSeq(tasksNode))
        return;
    const taskNames = new Set();
    const allTaskNames = [];
    for (const item of tasksNode.items) {
        if (!isMap(item))
            continue;
        const nameNode = findPair(item, "name");
        const nameVal = findScalarValue(item, "name");
        const kindVal = findScalarValue(item, "kind");
        if (!nameVal) {
            const range = rangeOf(item, yaml);
            diags.push({ from: range[0], to: range[1], severity: "error", message: "Task is missing required field: name" });
            continue;
        }
        if (!TASK_NAME_PATTERN.test(nameVal)) {
            const range = nameNode ? rangeOfPair(nameNode, yaml) : rangeOf(item, yaml);
            diags.push({ from: range[0], to: range[1], severity: "error", message: `Task name "${nameVal}": ${TASK_NAME_PATTERN_ERROR}` });
        }
        if (taskNames.has(nameVal)) {
            const range = nameNode ? rangeOfPair(nameNode, yaml) : rangeOf(item, yaml);
            diags.push({ from: range[0], to: range[1], severity: "warning", message: `Duplicate task name: "${nameVal}"` });
        }
        taskNames.add(nameVal);
        allTaskNames.push(nameVal);
        if (!kindVal) {
            const range = rangeOf(item, yaml);
            diags.push({ from: range[0], to: range[1], severity: "error", message: `Task "${nameVal}" is missing required field: kind` });
            continue;
        }
        if (!VALID_TASK_KINDS.has(kindVal)) {
            const kindPair = findPair(item, "kind");
            const range = kindPair ? rangeOfPair(kindPair, yaml) : rangeOf(item, yaml);
            diags.push({ from: range[0], to: range[1], severity: "error", message: `Task "${nameVal}" has unknown kind: "${kindVal}"` });
            continue;
        }
        const configNode = findMapValue(item, "task_config") ?? findMapValue(item, "taskConfig");
        if (!configNode || !isMap(configNode)) {
            const range = rangeOf(item, yaml);
            diags.push({ from: range[0], to: range[1], severity: "error", message: `Task "${nameVal}" is missing required field: task_config` });
        }
        else if (kindVal === "eval") {
            validateEvalConfig(configNode, nameVal, diags, yaml);
        }
        validateFlowReferences(item, nameVal, allTaskNames, taskNames, diags, yaml);
    }
    // Second pass: validate all flow.then references point to existing tasks
    for (const item of tasksNode.items) {
        if (!isMap(item))
            continue;
        const nameVal = findScalarValue(item, "name");
        if (!nameVal)
            continue;
        validateFlowReferencesSecondPass(item, nameVal, taskNames, diags, yaml);
    }
}
function validateFlowReferences(_taskNode, _taskName, _allNames, _nameSet, _diags, _yaml) {
    // Placeholder — forward references are validated in the second pass
}
function validateFlowReferencesSecondPass(taskNode, taskName, taskNames, diags, yaml) {
    const flowNode = findMapValue(taskNode, "flow");
    if (!flowNode || !isMap(flowNode))
        return;
    const thenVal = findScalarValue(flowNode, "then");
    if (!thenVal || thenVal === "end")
        return;
    if (!taskNames.has(thenVal)) {
        const thenPair = findPair(flowNode, "then");
        const range = thenPair ? rangeOfPair(thenPair, yaml) : rangeOf(flowNode, yaml);
        diags.push({
            from: range[0],
            to: range[1],
            severity: "warning",
            message: `Task "${taskName}" flow.then references unknown task "${thenVal}"`,
        });
    }
}
// ---------------------------------------------------------------------------
// Kind-specific config validation
// ---------------------------------------------------------------------------
function validateEvalConfig(configNode, taskName, diags, yaml) {
    for (const field of ["model", "subject", "rubric"]) {
        const val = findScalarValue(configNode, field);
        if (!val) {
            diags.push(diagAtKey(configNode, field, yaml, "error", `Eval task "${taskName}" is missing required field: ${field}`));
        }
    }
    const thresholdNode = findMapValue(configNode, "threshold");
    if (thresholdNode && isScalar(thresholdNode)) {
        const num = Number(thresholdNode.value);
        if (!Number.isFinite(num) || num < 0 || num > 1) {
            const pair = findPair(configNode, "threshold");
            const range = pair ? rangeOfPair(pair, yaml) : rangeOf(configNode, yaml);
            diags.push({ from: range[0], to: range[1], severity: "error", message: `Eval task "${taskName}": threshold must be between 0.0 and 1.0` });
        }
    }
}
// ---------------------------------------------------------------------------
// YAML node helpers
// ---------------------------------------------------------------------------
function findPair(map, key) {
    for (const item of map.items) {
        if (isScalar(item.key) && item.key.value === key) {
            return item;
        }
    }
    return undefined;
}
function findMapValue(map, key) {
    const pair = findPair(map, key);
    return pair?.value ?? undefined;
}
function findScalarValue(map, key) {
    const val = findMapValue(map, key);
    if (isScalar(val) && typeof val.value === "string")
        return val.value;
    return undefined;
}
function rangeOf(node, yaml) {
    if (node && typeof node === "object" && "range" in node) {
        const r = node.range;
        if (r)
            return [r[0], Math.min(r[1], yaml.length)];
    }
    return [0, Math.min(yaml.length, 1)];
}
function rangeOfPair(pair, yaml) {
    const keyRange = rangeOf(pair.key, yaml);
    const valRange = rangeOf(pair.value, yaml);
    return [keyRange[0], Math.max(valRange[1], keyRange[1])];
}
function diagAtKey(map, key, yaml, severity, message) {
    const pair = findPair(map, key);
    if (pair) {
        const range = rangeOfPair(pair, yaml);
        return { from: range[0], to: range[1], severity, message };
    }
    const range = rangeOf(map, yaml);
    return { from: range[0], to: Math.min(range[0] + 1, yaml.length), severity, message };
}
//# sourceMappingURL=useWorkflowValidation.js.map