/**
 * WorkerBindingStore — append-only binding revisions as readable Markdown (WP-02).
 */

import { promises as fs } from "node:fs";
import { basename, join } from "node:path";

import {
  parseMarkdownFrontmatter,
  renderFrontmatter,
} from "../../ledger/frontmatter.ts";
import { AtomicFile } from "../atomic/AtomicFile.ts";
import type { WorkerBindingRevision } from "../contracts/WorkerBinding.ts";
import { SwitchboardPaths } from "../paths/SwitchboardPaths.ts";

function stampFromIso(iso: string): string {
  return iso.replace(/[:.]/g, "-");
}

function bindingBody(rev: WorkerBindingRevision): string {
  return [
    renderFrontmatter({
      protocol: "codeflowmu.switchboard",
      schema: rev.schema,
      binding_id: rev.bindingId,
      revision: rev.revision,
      worker_id: rev.workerId,
      state: rev.state,
      host_id: rev.host.hostId,
      external_session_id: rev.host.externalSessionId,
      created_at: rev.createdAt,
      created_by: rev.createdBy,
      ...(rev.reason ? { reason: rev.reason } : {}),
      ...(rev.previousRevisionRef
        ? { previous_revision_ref: rev.previousRevisionRef }
        : {}),
    }),
    "",
    `# Worker Binding ${rev.bindingId} r${rev.revision}`,
    "",
    "```json",
    JSON.stringify(rev, null, 2),
    "```",
    "",
  ].join("\n");
}

export class WorkerBindingStore {
  readonly #paths: SwitchboardPaths;

  constructor(paths: SwitchboardPaths) {
    this.#paths = paths;
  }

  async append(revision: WorkerBindingRevision): Promise<string> {
    const path = this.#paths.bindingFile(
      revision.workerId,
      stampFromIso(revision.createdAt),
      revision.revision,
    );
    await AtomicFile.writeText(path, bindingBody(revision));
    return path;
  }

  async list(workerId: string): Promise<WorkerBindingRevision[]> {
    const dir = this.#paths.bindingsDir(workerId);
    let names: string[] = [];
    try {
      names = await fs.readdir(dir);
    } catch {
      return [];
    }
    const out: WorkerBindingRevision[] = [];
    for (const name of names.sort()) {
      if (!name.endsWith(".md") || name.includes(".tmp")) continue;
      const raw = await AtomicFile.readText(join(dir, name));
      const jsonMatch = raw.match(/```json\s*([\s\S]*?)```/);
      if (jsonMatch?.[1]) {
        out.push(JSON.parse(jsonMatch[1]) as WorkerBindingRevision);
        continue;
      }
      const fm = parseMarkdownFrontmatter(raw);
      out.push({
        schema: "codeflowmu.worker-binding/v1",
        bindingId: String(fm["binding_id"] ?? basename(name, ".md")),
        revision: Number(fm["revision"] ?? 0),
        workerId: String(fm["worker_id"] ?? workerId),
        host: {
          hostId: String(fm["host_id"] ?? ""),
          externalSessionId: String(fm["external_session_id"] ?? ""),
        },
        state: (String(fm["state"] ?? "ready") as WorkerBindingRevision["state"]),
        createdAt: String(fm["created_at"] ?? ""),
        createdBy: String(fm["created_by"] ?? ""),
        reason: fm["reason"] ? String(fm["reason"]) : undefined,
        previousRevisionRef: fm["previous_revision_ref"]
          ? String(fm["previous_revision_ref"])
          : undefined,
      });
    }
    return out.sort((a, b) => a.revision - b.revision);
  }

  async latest(workerId: string): Promise<WorkerBindingRevision | null> {
    const all = await this.list(workerId);
    return all.length ? all[all.length - 1]! : null;
  }
}
