import { readFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { join } from 'node:path';

// Hand the serve's NEW api surface the provider key it cannot find on its own.
//
// ── The problem this exists to prevent ──
//
// opencode keeps credentials in two unrelated places. The CLI, `opencode run` and the legacy
// `/session/*` surface read `~/.local/share/opencode/auth.json`. The newer `/api/session/*` surface —
// the one with `delivery: "steer" | "queue"`, `/interrupt` and a resumable per-session event stream —
// reads its own integration store instead (`/api/integration`, `/api/credential`), and knows nothing
// about that file.
//
// With no credential the new pipeline does not fail. It falls back to whatever needs none, which is the
// free tier, and a request for a paid model is simply never executed: prompt accepted, `prompt.admitted`
// and `prompted` emitted, no step, no error, no assistant message, forever. That silence cost most of an
// afternoon to diagnose (docs/opencode-fork-decision.md) and would cost it again on every new machine.
//
// So the sidecar connects it at start-up rather than relying on somebody having run a curl by hand.
//
// ── Deliberately best-effort ──
//
// Never throws and never blocks start-up. Turns run through `opencode run`, which uses `auth.json` and
// is unaffected by any of this; failing here costs the new pipeline only, and the sidecar is far more
// useful up than down. The connection persists in opencode's own store, so this is a no-op on every
// start after the first.

const AUTH_PATH = join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'opencode', 'auth.json');

const ATTEMPTS = 6;
const RETRY_DELAY_MS = 1_500;

/** The provider key opencode already holds for itself, or null. Never logged, never returned to callers. */
function readProviderKey(providerId: string): string | null {
  try {
    const auth = JSON.parse(readFileSync(AUTH_PATH, 'utf8')) as Record<string, { type?: string; key?: string }>;
    const entry = auth[providerId];
    return entry?.type === 'api' && typeof entry.key === 'string' && entry.key ? entry.key : null;
  } catch {
    return null; // no auth file, unreadable, or not JSON — nothing to connect
  }
}

/**
 * Connect `auth.json`'s key for one provider to the serve's integration store.
 *
 * `providerId` doubles as the integration id: opencode names them the same, so the Zen key stored under
 * `opencode` connects to integration `opencode`.
 */
export async function connectProviderCredential(baseUrl: string, providerId = 'opencode'): Promise<void> {
  const key = readProviderKey(providerId);
  if (!key) {
    console.log(`[opencode] no ${providerId} key in auth.json; the new API surface will only reach free models`);
    return;
  }

  // Retried, because `/api/health` answers before the integration store is ready: connecting immediately
  // after the health check returns 500, and the identical request succeeds seconds later. Measured, not
  // assumed — the first version of this shipped without the retry and failed on its first real boot.
  //
  // Only 5xx is retried. A 4xx means the request itself is wrong (bad key, unknown integration) and
  // repeating it just prints the same complaint five times.
  for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
    try {
      const res = await fetch(`${baseUrl}/api/integration/${providerId}/connect/key`, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ key, label: 'officer-opencode sidecar' }),
        signal: AbortSignal.timeout(10_000),
      });

      if (res.ok) {
        console.log(`[opencode] connected the ${providerId} credential to the api surface`);
        return;
      }
      // The body is deliberately not logged: a credential endpoint's error may quote what it was given.
      if (res.status < 500) {
        console.error(`[opencode] could not connect the ${providerId} credential: HTTP ${res.status}`);
        return;
      }
      if (attempt === ATTEMPTS) {
        console.error(
          `[opencode] could not connect the ${providerId} credential after ${ATTEMPTS} attempts: HTTP ${res.status}`,
        );
        return;
      }
    } catch (err) {
      if (attempt === ATTEMPTS) {
        console.error(
          `[opencode] could not connect the ${providerId} credential:`,
          err instanceof Error ? err.message : err,
        );
        return;
      }
    }
    await Bun.sleep(RETRY_DELAY_MS);
  }
}
