import { getSession } from "../lib/neo4j.js";
import { computeEmbedding } from "../lib/embeddings.js";
export async function workUpdate(params) {
    const { taskId, accountId, name, description, status, priority, dueDate, note, appendStep, errorMessage } = params;
    if (status === "failed" && (!errorMessage || errorMessage.trim().length === 0)) {
        throw new Error("task-update: errorMessage is required when status transitions to 'failed'.");
    }
    const session = getSession();
    try {
        // Fetch current task to build updated embedding and append note. Also
        // fetches `kind` so the lifecycle log line below knows whether this
        // Task is an action record (kind set) vs commitment-tracking (kind null).
        const existing = await session.run(`MATCH (t:Task {taskId: $taskId, accountId: $accountId})
       RETURN t.name AS name, t.description AS description, t.notes AS notes,
              t.status AS status, t.priority AS priority, t.kind AS kind,
              size(coalesce(t.steps, [])) AS stepsCount`, { taskId, accountId });
        if (existing.records.length === 0) {
            throw new Error(`Task not found: ${taskId}`);
        }
        const rec = existing.records[0];
        const currentName = rec.get("name");
        const currentDescription = rec.get("description");
        const currentNotes = rec.get("notes") ?? "";
        const currentStatus = rec.get("status");
        const currentPriority = rec.get("priority");
        const kind = rec.get("kind") ?? null;
        const newName = name ?? currentName;
        const newDescription = description ?? currentDescription;
        const newStatus = status ?? currentStatus;
        const newPriority = priority ?? currentPriority;
        // Append note with timestamp (append-only — never overwrite)
        let newNotes = currentNotes;
        if (note) {
            const timestamp = new Date().toISOString().slice(0, 16).replace("T", " ");
            newNotes = currentNotes
                ? `${currentNotes}\n[${timestamp}] ${note}`
                : `[${timestamp}] ${note}`;
        }
        const nowIso = new Date().toISOString();
        const setClauses = [
            "t.status = $status",
            "t.priority = $priority",
            "t.notes = $notes",
            "t.updatedAt = $updatedAt",
        ];
        const queryParams = {
            taskId,
            accountId,
            status: newStatus,
            priority: newPriority,
            notes: newNotes,
            updatedAt: nowIso,
        };
        if (name !== undefined) {
            setClauses.push("t.name = $name");
            queryParams.name = name;
        }
        if (description !== undefined) {
            setClauses.push("t.description = $description");
            queryParams.description = description;
        }
        if (dueDate !== undefined) {
            setClauses.push("t.dueDate = $dueDate");
            queryParams.dueDate = dueDate;
        }
        // atomic steps[] append. Cypher `coalesce(t.steps, []) + $step`
        // is one round-trip; no read-modify-write race even under concurrent
        // appends (executeWrite serializes per-tx). Distinct from notes which
        // is a free-form string field; steps is a structured action-phase log.
        if (appendStep !== undefined) {
            setClauses.push("t.steps = coalesce(t.steps, []) + $appendStep");
            queryParams.appendStep = appendStep;
        }
        // completion lifecycle. completedAt set on terminal status
        // transitions (completed | failed). errorMessage only persisted on
        // `failed` (the contract above already validates non-empty).
        if (status === "completed" || status === "failed") {
            setClauses.push("t.completedAt = $completedAt");
            queryParams.completedAt = nowIso;
        }
        if (status === "failed") {
            setClauses.push("t.errorMessage = $errorMessage");
            queryParams.errorMessage = errorMessage;
        }
        // Re-embed with updated values (substitute new name/description when provided)
        const embeddingText = `Task: ${newName} — ${newDescription} status: ${newStatus} priority: ${newPriority}${newNotes ? " " + newNotes : ""}`;
        const embedding = await computeEmbedding(embeddingText);
        if (embedding.length > 0) {
            setClauses.push("t.embedding = $embedding");
            queryParams.embedding = embedding;
        }
        const updateRes = await session.run(`MATCH (t:Task {taskId: $taskId, accountId: $accountId})
       SET ${setClauses.join(", ")}
       RETURN size(coalesce(t.steps, [])) AS stepsCount`, queryParams);
        // Lifecycle log lines — only for action Tasks (kind set). Commitment-
        // tracking callers (no kind) stay silent on this channel so existing
        // [task] grep targets aren't flooded with non-action noise.
        if (kind) {
            if (appendStep !== undefined) {
                process.stderr.write(`[task] action-step kind=${kind} taskId=${taskId} step=${appendStep}\n`);
            }
            if (status === "completed" || status === "failed") {
                const stepsCount = updateRes.records[0]?.get("stepsCount")?.toNumber?.() ?? 0;
                process.stderr.write(`[task] action-done kind=${kind} taskId=${taskId} status=${status} stepsCount=${stepsCount}\n`);
            }
        }
    }
    finally {
        await session.close();
    }
}
//# sourceMappingURL=work-update.js.map