/**
 * @internal
 * coordination/store.mjs — thin abstraction seam for coordination writes (R8).
 *
 * IMPORTANT: this module is `@internal`. It is NOT exported from
 * `bin/apt-tools.mjs`, it is NOT added to any package `exports` map, and it
 * must not appear in README / public docs. The seam exists so future
 * subtasks (R2 team-status dir, R4 liveness, R10 event log) have a single
 * place to swap implementations without every call site needing an edit.
 *
 * V1 contract: every method is a thin wrapper over the existing filesystem
 * helpers (`atomicWriteJson`, `withFileLock`). Behavior must match the
 * pre-seam call sites byte-for-byte — C29's smoke-drain harness (retired in
 * C30) originally guarded against regressions; the vitest suite under
 * src/__tests__ now owns that coverage.
 *
 * Methods:
 *   - readState()
 *   - writeState(next) / updateState(mutator)
 *   - upsertTeamStatus(taskId, data) / removeTeamStatus(taskId)
 *   - listTeamStatus({ includeStale? })            — filled in by R2
 *   - appendEvent(evt)                             — filled in by R10
 *   - resolveSharedRoot()                          — main repo root helper
 *
 * Callers that mutate state should use `updateState(mutator)` (lock-protected
 * read-modify-write). Callers that only read should use `readState()`.
 */
import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import { withFileLock } from '../util/fs-lock.mjs';
import { atomicWriteJson } from '../util/io.mjs';
import { getAgentIdentity, resolveSharedRoot } from './agent-identity.mjs';
import { appendLifecycleEvent } from './event-log.mjs';
import { validateConductorEvent } from './event-schema.mjs';
import { listActive as teamStatusListActive, removeSelf as teamStatusRemoveSelf, upsertSelf as teamStatusUpsertSelf, } from './team-status.mjs';
/**
 * Construct a coordination store bound to a specific project root. The
 * returned object is plain-JS with bound methods — no classes, matching the
 * rest of the `.mjs` CLI kernel style.
 *
 * @internal
 */
export function createCoordinationStore(projectRoot) {
    const sharedRoot = resolveSharedRoot(projectRoot);
    const statePath = join(sharedRoot, '.aperant', 'state.json');
    return {
        /** @internal */
        resolveSharedRoot: () => sharedRoot,
        /** @internal — returns null if state.json is missing or malformed. */
        readState() {
            if (!existsSync(statePath))
                return null;
            try {
                return JSON.parse(readFileSync(statePath, 'utf-8'));
            }
            catch {
                return null;
            }
        },
        /**
         * @internal — lock-protected atomic write of the full state object.
         * Prefer `updateState(mutator)` for read-modify-write.
         */
        writeState(state) {
            withFileLock(statePath, () => {
                atomicWriteJson(statePath, state);
            }, { projectRoot: sharedRoot });
        },
        /**
         * @internal — read, mutate via `mutator(state)`, write back under a
         * single lock. Mirrors the pattern used throughout commands/task.mjs.
         */
        updateState(mutator) {
            return withFileLock(statePath, () => {
                let state = null;
                if (existsSync(statePath)) {
                    try {
                        state = JSON.parse(readFileSync(statePath, 'utf-8'));
                    }
                    catch {
                        state = null;
                    }
                }
                const result = mutator(state);
                if (result !== undefined)
                    atomicWriteJson(statePath, result);
                return result;
            }, { projectRoot: sharedRoot });
        },
        /**
         * @internal — R2: per-agent team-status/{agentId}.json writes.
         * C34: the legacy active.json dual-write has been removed; the
         * per-agent directory is the sole source of truth.
         */
        upsertTeamStatus(taskId, data = {}) {
            teamStatusUpsertSelf(projectRoot, taskId, data);
        },
        /** @internal — see upsertTeamStatus. */
        removeTeamStatus(taskId) {
            teamStatusRemoveSelf(projectRoot, taskId);
        },
        /**
         * @internal — R2 directory aggregation + R4 liveness filtering.
         * Pass `{ includeStale: true }` to disable stale-filtering (used by
         * `apt-tools active list --include-stale`).
         */
        listTeamStatus(opts = {}) {
            return teamStatusListActive(projectRoot, opts);
        },
        /**
         * @internal — R10 append-only event log. Emits one JSONL line per
         * call via appendLifecycleEvent. Best-effort: filesystem errors are
         * swallowed so a missing events/ dir never breaks task lifecycle.
         *
         * Step 1 — schema gate: conductor ops written via the store seam are
         * validated against `event-schema.mjs` before they reach disk.
         * Schema violations throw (caught here and surfaced via console.warn)
         * because a malformed conductor event would silently break the
         * drawer's pairing logic — we want it loud, not best-effort.
         */
        appendEvent(evt) {
            try {
                if (evt && typeof evt.op === 'string') {
                    const validation = validateConductorEvent(evt.op, evt.data);
                    if (!validation.ok) {
                        console.warn(`[store.appendEvent] schema violation: ${validation.error}`);
                        return null;
                    }
                }
                return appendLifecycleEvent(projectRoot, evt);
            }
            catch {
                /* best-effort — never block a lifecycle transition on the log */
                return null;
            }
        },
        /** @internal */
        getIdentity() {
            return getAgentIdentity(projectRoot);
        },
    };
}
//# sourceMappingURL=store.mjs.map