/**
 * WP-13 — observation snapshot for Panel / PWA / Mobile (read-only).
 *
 * Aggregates Host / Binding / Run / Delivery / instance / roots / locks /
 * Gateway / isolation alerts. Does not enable Active delivery.
 */

import { DoorbellMailbox } from "../store/DoorbellMailbox.js";
import { RunStore } from "../store/RunStore.js";
import { WorkerBindingStore } from "../store/WorkerBindingStore.js";
import { SwitchboardPaths } from "../paths/SwitchboardPaths.js";
import type { EvidenceObservationFields } from "./EvidenceObservationFields.js";

export interface WorkerObservationRow {
  workerId: string;
  hostId: string | "UNKNOWN";
  bindingId: string | "UNKNOWN";
  bindingState: string | "UNKNOWN";
  hostSessionOk: boolean | "UNKNOWN";
  continuityMode: EvidenceObservationFields["continuity_mode"] | "UNKNOWN";
  pendingDoorbells: number;
  failedDoorbells: number;
  deliveryUnknownRuns: number;
  activeRuns: number;
}

export interface ObservationSnapshot {
  schema: "codeflowmu.observation-snapshot/v1";
  generatedAt: string;
  instanceId: string | "UNKNOWN";
  runtimeRole: string | "UNKNOWN";
  registryRoot: string | "UNKNOWN";
  dataRoot: string | "UNKNOWN";
  fcopRoot: string | "UNKNOWN";
  writerLock: {
    held: boolean | "UNKNOWN";
    holder?: string;
    path?: string;
  };
  gateway: {
    status: "up" | "down" | "degraded" | "UNKNOWN";
    detail?: string;
  };
  isolationAlerts: string[];
  workers: WorkerObservationRow[];
  /** Explicit: observation only — not a delivery cutover signal. */
  productionActiveEnabled: false;
}

export interface ObservationSnapshotInput {
  paths: SwitchboardPaths;
  workerIds: string[];
  instanceId?: string;
  runtimeRole?: string;
  registryRoot?: string;
  dataRoot?: string;
  fcopRoot?: string;
  writerLock?: ObservationSnapshot["writerLock"];
  gateway?: ObservationSnapshot["gateway"];
  isolationAlerts?: string[];
  now?: () => Date;
}

export class ObservationSnapshotBuilder {
  async build(input: ObservationSnapshotInput): Promise<ObservationSnapshot> {
    const mailbox = new DoorbellMailbox(input.paths);
    const bindings = new WorkerBindingStore(input.paths);
    const runs = new RunStore(input.paths);
    const workers: WorkerObservationRow[] = [];

    for (const workerId of input.workerIds) {
      const pending = await mailbox.list(workerId, "pending");
      const failed = await mailbox.list(workerId, "failed");
      const latest = await bindings.latest(workerId);
      let activeRuns: Awaited<ReturnType<RunStore["list"]>> = [];
      let settled: Awaited<ReturnType<RunStore["list"]>> = [];
      try {
        activeRuns = await runs.list(workerId, "active");
        settled = await runs.list(workerId, "settled");
      } catch {
        /* empty */
      }
      const deliveryUnknown = settled.filter(
        (r) => r.state === "delivery_unknown",
      ).length;
      const continuity =
        activeRuns[0]?.continuationMode ??
        settled[settled.length - 1]?.continuationMode ??
        "UNKNOWN";

      workers.push({
        workerId,
        hostId: latest?.host.hostId ?? "UNKNOWN",
        bindingId: latest?.bindingId ?? "UNKNOWN",
        bindingState: latest?.state ?? "UNKNOWN",
        hostSessionOk: latest ? latest.state === "ready" : "UNKNOWN",
        continuityMode: continuity,
        pendingDoorbells: pending.length,
        failedDoorbells: failed.length,
        deliveryUnknownRuns: deliveryUnknown,
        activeRuns: activeRuns.length,
      });
    }

    return {
      schema: "codeflowmu.observation-snapshot/v1",
      generatedAt: (input.now?.() ?? new Date()).toISOString(),
      instanceId: input.instanceId ?? "UNKNOWN",
      runtimeRole: input.runtimeRole ?? "UNKNOWN",
      registryRoot: input.registryRoot ?? "UNKNOWN",
      dataRoot: input.dataRoot ?? "UNKNOWN",
      fcopRoot: input.fcopRoot ?? "UNKNOWN",
      writerLock: input.writerLock ?? { held: "UNKNOWN" },
      gateway: input.gateway ?? { status: "UNKNOWN" },
      isolationAlerts: [...(input.isolationAlerts ?? [])],
      workers,
      productionActiveEnabled: false,
    };
  }
}

/** Field table for REPORT / QA (API sample shape). */
export const OBSERVATION_FIELD_TABLE: Array<{
  field: string;
  meaning_zh: string;
  source: string;
}> = [
  { field: "instance_id", meaning_zh: "Runtime 实例标识", source: "snapshot.instanceId" },
  { field: "runtime_role", meaning_zh: "运行角色（如 DEV-01 / Runtime）", source: "snapshot.runtimeRole" },
  { field: "registry_root", meaning_zh: "Agent registry 根路径", source: "snapshot.registryRoot" },
  { field: "data_root", meaning_zh: "数据根路径", source: "snapshot.dataRoot" },
  { field: "fcop_root", meaning_zh: "FCoP 账本根", source: "snapshot.fcopRoot" },
  { field: "writer_lock", meaning_zh: "写锁占用状态", source: "snapshot.writerLock" },
  { field: "gateway.status", meaning_zh: "Gateway 可达性", source: "snapshot.gateway" },
  { field: "isolation_alerts", meaning_zh: "隔离冲突告警", source: "snapshot.isolationAlerts" },
  { field: "workers[].host_id", meaning_zh: "Worker 当前 Host", source: "WorkerBinding" },
  { field: "workers[].binding_id", meaning_zh: "当前 Binding", source: "WorkerBinding" },
  { field: "workers[].host_session_ok", meaning_zh: "Host Session 是否正常", source: "binding.state" },
  { field: "workers[].continuity_mode", meaning_zh: "同 Session / 文件接手", source: "RunRecord" },
  { field: "workers[].pending_doorbells", meaning_zh: "pending Doorbell 数", source: "DoorbellMailbox" },
  { field: "workers[].failed_doorbells", meaning_zh: "failed Doorbell 数", source: "DoorbellMailbox" },
  { field: "workers[].delivery_unknown_runs", meaning_zh: "delivery_unknown Run 数", source: "RunStore" },
  { field: "production_active_enabled", meaning_zh: "是否启用生产 Active（本快照恒 false）", source: "hard constraint" },
];
