import type { ChatEvent } from '@@/api/chat/types';
import { logger } from '@@/api/chat/logger';
import * as sidecar from '@@/sidecar-registry';
import { getOpenCodeSession } from '@@/api/chat/opencode/state';

// The OpenCode analog of send-claude-code.ts: it drives a turn through the officer-opencode sidecar,
// which spawns `opencode run … --format json` (tools hard-anchored to the chat cwd via --dir) and
// streams mapped ChatEvents back over the sidecar WS. We subscribe to those events (filtered by
// sessionKey) and forward them to the caller's onEvent — the same shared contract the Claude harness
// uses, so createEventHandler and the whole UI pipeline are unchanged.

type OpenCodeStreamingParams = {
  userId: number;
  email: string;
  username: string;
  prompt: string;
  sessionKey: string;
  cwd?: string;
  model?: string;
  role?: string;
  resumeSessionId?: string;
  onEvent: (event: ChatEvent) => void;
};

type OpenCodeStreamingHandle = {
  kill: () => void;
};

export async function sendOpenCodeStreaming(params: OpenCodeStreamingParams): Promise<OpenCodeStreamingHandle> {
  logger.info('OpenCode streaming exec (via sidecar)', { sessionKey: params.sessionKey, model: params.model });

  // Forward this session's turn events; unsubscribe on the terminal event.
  const unsub = sidecar.onOpenCodeEvent((sessionKey, event) => {
    if (sessionKey !== params.sessionKey) return;
    params.onEvent(event);
    if (event.type === 'result' || event.type === 'error' || event.type === 'stopped') unsub();
  });

  // Resume an existing OpenCode session when we know its id: a stored mapping (set from the sidecar's
  // opencode:session report), the sessionKey itself when it's already a `ses_…` id (history resume), or
  // an explicit resumeSessionId. Otherwise the sidecar's `run` creates a fresh session.
  const resumeSessionId =
    getOpenCodeSession(params.sessionKey) ??
    (params.sessionKey.startsWith('ses_') ? params.sessionKey : undefined) ??
    params.resumeSessionId;

  try {
    await sidecar.spawnOpenCodeStreaming({
      sessionKey: params.sessionKey,
      prompt: params.prompt,
      cwd: params.cwd,
      model: params.model,
      resumeSessionId,
    });
  } catch (err) {
    unsub();
    throw err;
  }

  return {
    kill: () => {
      sidecar.killOpenCode(params.sessionKey);
      unsub();
    },
  };
}
