import { SESSIONS_PANEL_ID, sessionFromPanel, type SessionView } from "../adapters/sessions";
import type { CollectorState } from "../state";
import { captureScreen, sendKeys, tmuxBinary, type SendKeysInput, type TmuxSpawn } from "./tmux";
import { remoteTmuxSpawn, resolveReachableSshAlias } from "./remote";
import type { BuildboxRegistry } from "../buildbox-registry";

export interface SessionAttachDeps {
  binary?: () => string | null;
  spawn?: TmuxSpawn;
  loadRegistry?: () => Promise<BuildboxRegistry>;
  now?: () => number;
}

async function tmuxDeps(session: SessionView, deps: SessionAttachDeps) {
  // SessionView does not carry panel localHost, so hostFacts is non-null for both local and
  // remote records. The adapter's attach descriptor identifies remote live sessions exactly.
  const remote = session.attach.kind === "ssh";
  if (remote) {
    const alias = await resolveReachableSshAlias(session.host!, deps.loadRegistry);
    return { binary: "tmux", spawn: remoteTmuxSpawn(alias, deps.spawn) };
  }
  const binary = (deps.binary ?? tmuxBinary)();
  if (!binary) throw new Error("tmux is not installed on this host");
  return { binary, ...(deps.spawn ? { spawn: deps.spawn } : {}) };
}

export interface SessionScreenResponse {
  ok: boolean;
  /** Raw pane text with SGR escapes; empty when unavailable. */
  screen: string;
  capturedAt: string;
  sessionState?: string;
  error?: string;
}

/** Resolves a client-supplied session id against the live panel snapshot. The tmux
 * socket and session name are read from the ledger record — never from the request. */
export function resolveAttachTarget(
  state: CollectorState,
  sessionId: string,
): { session: SessionView } | { error: string; status: number } {
  const panel = state.getPanels().find((entry) => entry.id === SESSIONS_PANEL_ID);
  if (!panel) return { error: "sessions adapter has not reported yet", status: 503 };
  const session = sessionFromPanel(panel.data, sessionId);
  if (!session) return { error: "session not found", status: 404 };
  if (!session.attachable) {
    return { error: session.attachBlockedReason ?? "session is not attachable", status: 409 };
  }
  if (!session.tmuxSession || !session.tmuxSocket) {
    return { error: "ledger entry has no tmux target", status: 409 };
  }
  return { session };
}

export async function readSessionScreen(
  state: CollectorState,
  sessionId: string,
  deps: SessionAttachDeps = {},
): Promise<{ status: number; body: SessionScreenResponse }> {
  const now = deps.now ?? Date.now;
  const capturedAt = new Date(now()).toISOString();
  const resolved = resolveAttachTarget(state, sessionId);
  if ("error" in resolved) {
    return { status: resolved.status, body: { ok: false, screen: "", capturedAt, error: resolved.error } };
  }

  let commandDeps;
  try {
    commandDeps = await tmuxDeps(resolved.session, deps);
  } catch (error) {
    return {
      status: 503,
      body: { ok: false, screen: "", capturedAt, error: (error as Error).message },
    };
  }

  const capture = await captureScreen(
    { socket: resolved.session.tmuxSocket!, session: resolved.session.tmuxSession! },
    commandDeps,
  );
  if (!capture.ok) {
    return {
      status: 502,
      body: { ok: false, screen: "", capturedAt, error: capture.error ?? "tmux capture failed" },
    };
  }

  return {
    status: 200,
    body: {
      ok: true,
      screen: capture.screen,
      capturedAt,
      sessionState: resolved.session.state,
    },
  };
}

export async function writeSessionKeys(
  state: CollectorState,
  sessionId: string,
  input: SendKeysInput,
  deps: SessionAttachDeps = {},
): Promise<{ status: number; rc: number; result: string }> {
  const resolved = resolveAttachTarget(state, sessionId);
  if ("error" in resolved) return { status: resolved.status, rc: resolved.status, result: resolved.error };

  let commandDeps;
  try {
    commandDeps = await tmuxDeps(resolved.session, deps);
  } catch (error) {
    return { status: 503, rc: 503, result: (error as Error).message };
  }

  const sent = await sendKeys(
    { socket: resolved.session.tmuxSocket!, session: resolved.session.tmuxSession! },
    input,
    commandDeps,
  );
  if (sent.rc !== 0) {
    return { status: 502, rc: sent.rc, result: sent.stderr || `tmux send-keys exited ${sent.rc}` };
  }
  return { status: 200, rc: 0, result: "ok" };
}
