// v2.6.10 Phase 3 (server wiring) — fire-and-forget BooCoder close hooks.
//
// BooCoder (apps/coder, host systemd) added close hooks in
// apps/coder/src/routes/lifecycle.ts:
//   POST /api/chats/:chatId/close      — evict the chat's warm (chat,agent)
//                                        backends, close its opencode session,
//                                        mark agent_sessions closed, and remove
//                                        the shared worktree on the last chat.
//   POST /api/sessions/:sessionId/close — loop the chat-close path for every
//                                        chat in the session.
//
// apps/server (Docker) can't see the host worktree dirs or reach the warm agent
// processes, so — exactly like the existing `worktree-risk` guard in
// routes/sessions.ts — it signals the coder over HTTP and the coder does the
// real teardown. This call is BEST-EFFORT: the coder's idle-pool eviction and
// the orphan-worktree reaper backstop a missed/failed call. It MUST NEVER block
// or fail the user's delete/archive — hence fire-and-forget with a swallowed
// catch. We do not await the returned promise at the call sites.

import type { FastifyBaseLogger } from 'fastify';

export type CoderCloseKind = 'chat' | 'session';

function coderOrigin(): string {
  // Same env + default as routes/sessions.ts' worktree-risk fetch.
  return process.env.BOOCODER_URL ?? 'http://boocoder:3000';
}

/**
 * Fire-and-forget POST to the BooCoder close hook for a chat or session.
 *
 * Resolves to `true` if the coder acknowledged (HTTP 2xx), `false` otherwise
 * (non-2xx or network error). Callers SHOULD NOT await this — invoke it and
 * move on. The returned promise never rejects: every failure path is caught,
 * logged at debug, and folded into a `false` result so an unreachable or
 * erroring coder can't surface to the user's delete/archive request.
 */
export async function notifyCoderClose(
  kind: CoderCloseKind,
  id: string,
  log?: Pick<FastifyBaseLogger, 'debug'>,
  fetcher: typeof fetch = fetch,
): Promise<boolean> {
  const segment = kind === 'chat' ? 'chats' : 'sessions';
  const url = `${coderOrigin()}/api/${segment}/${id}/close`;
  try {
    const res = await fetcher(url, { method: 'POST' });
    if (!res.ok) {
      log?.debug(
        { kind, id, status: res.status },
        'coder close hook returned non-2xx (best-effort; reaper backstops)',
      );
      return false;
    }
    log?.debug({ kind, id }, 'coder close hook acknowledged');
    return true;
  } catch (err) {
    log?.debug(
      { kind, id, err: err instanceof Error ? err.message : String(err) },
      'coder close hook unreachable (best-effort; reaper backstops)',
    );
    return false;
  }
}
