import { createOpencodeClient } from "@opencode-ai/sdk";

export type OpencodeProbeConfig = {
  opencodeUrl?: string;
  opencodeUsername: string;
  opencodeServerPassword?: string;
  probeTimeoutMs?: number;
  // Injectable for unit tests; defaults to the SDK's global fetch.
  fetch?: typeof fetch;
};

export type ProbeResult = { ok: boolean; detail?: string };

export type OpencodeProbe = {
  probe(): Promise<ProbeResult>;
};

const DEFAULT_PROBE_TIMEOUT_MS = 5000;

// Builds the opencode client once; probe() reuses it and issues a fresh, 5s-bounded
// `config.get()` per call (no caching). No OPENCODE_URL => short-circuit without a call.
export function createOpencodeProbe(config: OpencodeProbeConfig): OpencodeProbe {
  const url = config.opencodeUrl;
  if (!url) {
    return {
      probe: () => Promise.resolve({ ok: false, detail: "OPENCODE_URL not set" }),
    };
  }

  const baseUrl = url.replace(/\/+$/, "");
  const timeoutMs = config.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;

  const headers: Record<string, string> = {};
  if (config.opencodeServerPassword) {
    const basic = Buffer.from(
      `${config.opencodeUsername}:${config.opencodeServerPassword}`,
    ).toString("base64");
    headers.Authorization = `Basic ${basic}`;
  }

  const client = createOpencodeClient({
    baseUrl,
    headers,
    throwOnError: false,
    ...(config.fetch ? { fetch: config.fetch } : {}),
  });

  return {
    async probe(): Promise<ProbeResult> {
      try {
        const res = await client.config.get({ signal: AbortSignal.timeout(timeoutMs) });
        if (res.response.ok) {
          return { ok: true };
        }
        return { ok: false, detail: `${baseUrl} -> ${res.response.status}` };
      } catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return { ok: false, detail: `${baseUrl} -> ${message}` };
      }
    },
  };
}
