import { invoke } from "@tauri-apps/api/core";

export type AgentBackendKind =
  | "anthropic"
  | "ollama"
  | "openai_api"
  | "codex_subscription"
  | "codex_native"
  | "pi_sdk"
  | "custom_anthropic"
  | "custom_openai"
  | "lm_studio";

export interface AgentBackendCapabilities {
  thinking: boolean;
  effort: boolean;
  fast_mode: boolean;
  one_m_context: boolean;
  tools: boolean;
  vision: boolean;
}

export interface AgentBackendModel {
  id: string;
  label: string;
  context_window_tokens: number;
  discovered: boolean;
}

/**
 * Which subprocess Claudette spawns for a chat turn on this backend.
 * Mirrors the Rust `AgentBackendRuntimeHarness` enum.
 *
 *  - `claude_code` — the bundled Claude CLI (with `ANTHROPIC_BASE_URL` /
 *    gateway env when the backend isn't Anthropic itself).
 *  - `codex_app_server` — the Codex CLI's debug app-server.
 *  - `pi_sdk` — Claudette's bundled Pi sidecar.
 */
export type AgentBackendRuntimeHarness =
  | "claude_code"
  | "codex_app_server"
  | "pi_sdk";

export interface AgentBackendConfig {
  id: string;
  label: string;
  kind: AgentBackendKind;
  base_url: string | null;
  enabled: boolean;
  default_model: string | null;
  manual_models: AgentBackendModel[];
  discovered_models: AgentBackendModel[];
  auth_ref: string | null;
  capabilities: AgentBackendCapabilities;
  context_window_default: number;
  model_discovery: boolean;
  has_secret: boolean;
  /** User override for which runtime harness handles this backend.
   *  `undefined` means use the kind's default — see `defaultHarnessForKind`. */
  runtime_harness?: AgentBackendRuntimeHarness | null;
}

export interface AgentBackendListResponse {
  backends: AgentBackendConfig[];
  default_backend_id: string;
  /**
   * Non-fatal diagnostics from the tolerant loader — e.g. a stored
   * backend entry whose `kind` isn't recognized by this build. The
   * entries are preserved in SQLite (so they round-trip through
   * downgrades), but the user should know they aren't active.
   * Omitted from the wire payload (i.e. `undefined` here) when
   * everything parsed cleanly — the Rust side uses
   * `skip_serializing_if = "Vec::is_empty"`, so consumers should
   * treat `undefined` and `[]` identically.
   */
  warnings?: string[];
}

export interface BackendSecretUpdate {
  backend_id: string;
  value: string | null;
}

export interface BackendStatus {
  ok: boolean;
  message: string;
  backends?: AgentBackendConfig[];
}

export function listAgentBackends(): Promise<AgentBackendListResponse> {
  return invoke("list_agent_backends");
}

export function autoDetectAgentBackends(): Promise<AgentBackendListResponse> {
  return invoke("auto_detect_agent_backends");
}

export function saveAgentBackend(
  backend: AgentBackendConfig,
): Promise<AgentBackendConfig[]> {
  return invoke("save_agent_backend", { backend });
}

export function deleteAgentBackend(
  backendId: string,
): Promise<AgentBackendConfig[]> {
  return invoke("delete_agent_backend", { backendId });
}

export function saveAgentBackendSecret(
  update: BackendSecretUpdate,
): Promise<void> {
  return invoke("save_agent_backend_secret", { update });
}

export function refreshAgentBackendModels(
  backendId: string,
): Promise<AgentBackendConfig[]> {
  return invoke("refresh_agent_backend_models", { backendId });
}

export function testAgentBackend(backendId: string): Promise<BackendStatus> {
  return invoke("test_agent_backend", { backendId });
}

export function launchCodexLogin(): Promise<void> {
  return invoke("launch_codex_login");
}

export function setAgentBackendRuntimeHarness(
  backendId: string,
  harness: AgentBackendRuntimeHarness | null,
): Promise<AgentBackendConfig[]> {
  return invoke("set_agent_backend_runtime_harness", { backendId, harness });
}

/**
 * Mirror of `AgentBackendKind::default_harness` for the frontend.
 * Keep in lockstep with `src/agent_backend.rs` — a Rust-side test
 * pins the matrix.
 */
export function defaultHarnessForKind(
  kind: AgentBackendKind,
): AgentBackendRuntimeHarness {
  switch (kind) {
    case "anthropic":
    case "custom_anthropic":
    case "codex_subscription":
    case "openai_api":
    case "custom_openai":
      return "claude_code";
    case "ollama":
    case "lm_studio":
      return "pi_sdk";
    case "codex_native":
      return "codex_app_server";
    case "pi_sdk":
      return "pi_sdk";
  }
}

/**
 * Mirror of `AgentBackendKind::available_harnesses`. The first entry is
 * the default. Pinning a value outside this list is rejected server-side
 * by `set_agent_backend_runtime_harness`.
 */
export function availableHarnessesForKind(
  kind: AgentBackendKind,
): AgentBackendRuntimeHarness[] {
  switch (kind) {
    case "anthropic":
    case "custom_anthropic":
    case "codex_subscription":
      return ["claude_code"];
    case "ollama":
    case "lm_studio":
      return ["pi_sdk", "claude_code"];
    case "openai_api":
    case "custom_openai":
      return ["claude_code", "pi_sdk"];
    case "codex_native":
      return ["codex_app_server", "pi_sdk"];
    case "pi_sdk":
      return ["pi_sdk"];
  }
}

/** Effective harness for a config: persisted override when allowed,
 *  otherwise the kind's default. Matches `AgentBackendConfig::effective_harness`. */
export function effectiveHarness(
  backend: AgentBackendConfig,
): AgentBackendRuntimeHarness {
  const override = backend.runtime_harness ?? undefined;
  if (override && availableHarnessesForKind(backend.kind).includes(override)) {
    return override;
  }
  return defaultHarnessForKind(backend.kind);
}
