import { isAttachableState, type SessionRecord } from "./ledger";
import type { HostFacts } from "./hosts";

/**
 * The per-CLI reconnect matrix. One place decides how (and whether) a given session can be
 * reached again, because the answer differs per runtime, per multiplexer and per host — and
 * a reconnect affordance that does nothing is worse than an honest refusal.
 *
 * `kind: "none"` ALWAYS carries a reason naming the missing fact, never a generic failure.
 */

export type AttachKind = "tmux" | "resume" | "ssh" | "none";

export interface AttachDescriptor {
  kind: AttachKind;
  /** What the command addresses: a mux session, a runtime session id, or a host. */
  target?: string;
  /** Verbatim command that reconnects. Absent only when kind is "none". */
  command?: string;
  /** Why reconnect is impossible. Present if and only if kind is "none". */
  reason?: string;
}

export interface AttachContext {
  /** Host the collector runs on — the only host whose processes it can see. */
  localHost: string;
  /** Registry facts for the session's host; null when the record carries no host. */
  hostFacts: HostFacts | null;
  /**
   * Runtime session ids currently held by a live session, mapped to that session's ledger
   * id. The ledger can hold several entries for one runtime session, so a stopped entry may
   * carry the same resume id as a running one; resuming it would attach a second client to
   * a session someone is using.
   */
  liveResumeIds?: ReadonlyMap<string, string>;
}

/** Key for `liveResumeIds`. Resume ids are only unique within a runtime. */
export function resumeKey(record: SessionRecord): string | null {
  return record.resumeId ? `${record.runtime}:${record.resumeId}` : null;
}

/** User-facing reopen verb. docs/agent-session-ledger.md forbids printing dtach/tmux. */
function reopenCommand(record: SessionRecord): string {
  return `agent-sessions attach ${record.id}`;
}

function resumeCommand(record: SessionRecord): AttachDescriptor {
  const id = record.resumeId;
  if (!id) {
    return {
      kind: "none",
      reason: `no session id was recorded for this ${record.runtime} session, so it cannot be resumed by id`,
    };
  }
  switch (record.runtime) {
    case "claude":
      // `cld` is the account-aware launcher. The recorder writes no account for a session
      // on the default account, so an absent one is complete information, not a gap.
      return {
        kind: "resume",
        target: id,
        command: record.account
          ? `cld --account ${record.account} --resume ${id}`
          : `cld --resume ${id}`,
      };
    case "codex":
      return { kind: "resume", target: id, command: `codex resume ${id}` };
    case "cursor-agent":
      return { kind: "resume", target: id, command: `cursor-agent --resume ${id}` };
    default:
      return {
        kind: "none",
        reason: `no resume verb is known for runtime "${record.runtime}"`,
      };
  }
}

function remote(record: SessionRecord, host: string, facts: HostFacts | null): AttachDescriptor | null {
  if (facts?.reachability === "declared-disabled") {
    return {
      kind: "none",
      reason: `host ${host} is disabled in the buildbox registry${facts.notes ? ` (${facts.notes})` : ""}`,
    };
  }
  return null;
}

export function resolveAttach(record: SessionRecord, context: AttachContext): AttachDescriptor {
  const host = record.host ?? null;
  const offHost = host !== null && host !== context.localHost;

  if (offHost) {
    const blocked = remote(record, host!, context.hostFacts);
    if (blocked) return blocked;
  }

  if (isAttachableState(record.state)) {
    const inner = record.tmuxSession && record.tmuxSocket
      ? { kind: "tmux" as const, target: record.tmuxSession, command: reopenCommand(record) }
      : record.mux?.kind && record.mux.socket
        ? { kind: "tmux" as const, target: record.mux.target ?? record.id, command: reopenCommand(record) }
        : null;
    if (!inner) {
      return {
        kind: "none",
        reason: "this session was started without a multiplexer, so there is no window to reattach to",
      };
    }
    if (!offHost) return inner;
    return { kind: "ssh", target: host!, command: `ssh ${host} ${inner.command}` };
  }

  const resumed = resumeCommand(record);
  const key = resumeKey(record);
  const heldBy = key ? context.liveResumeIds?.get(key) : undefined;
  if (heldBy && heldBy !== record.id) {
    return {
      kind: "none",
      reason: `session id ${record.resumeId} is in use by a live session (${heldBy}) — resuming it would attach a second client`,
    };
  }
  if (!offHost || resumed.kind === "none") return resumed;
  return { kind: "ssh", target: host!, command: `ssh ${host} ${resumed.command}` };
}

export interface ScreenCapability {
  attachable: boolean;
  reason?: string;
}

/**
 * Whether the collector can render this session's live screen in the browser. Narrower
 * than `resolveAttach`: only a tmux-hosted session can be snapshotted with capture-pane,
 * locally or through a registry-approved remote host.
 */
export function resolveScreenCapability(
  record: SessionRecord,
  context: AttachContext & { tmuxAvailable: boolean; attachAllowed: boolean; attachBlockedReason?: string },
): ScreenCapability {
  if (!context.attachAllowed) {
    return { attachable: false, reason: context.attachBlockedReason ?? "browser attach disabled" };
  }
  const host = record.host ?? null;
  const offHost = host !== null && host !== context.localHost;
  if (offHost && context.hostFacts?.reachability !== "declared-enabled") {
    const detail = context.hostFacts?.state ? ` (state: ${context.hostFacts.state})` : "";
    return { attachable: false, reason: `${host} is not marked reachable in the buildbox registry${detail}` };
  }
  if (!isAttachableState(record.state)) {
    return { attachable: false, reason: `session is ${record.state}` };
  }
  if (!record.tmuxSession || !record.tmuxSocket) {
    return { attachable: false, reason: "this session is not hosted in tmux, so its screen cannot be rendered in the browser" };
  }
  if (!offHost && !context.tmuxAvailable) {
    return { attachable: false, reason: "tmux is not installed on this host" };
  }
  return { attachable: true };
}
