import { invoke } from "@tauri-apps/api/core";
import { hasTauriRuntime } from "$lib/utils/runtime.js";

/// Visible LLM state for the status chip (brief item #31).
///   off        — user has settings.aiTier === "off"; chip hidden.
///   warming    — model download or load in progress.
///   ready      — model loaded, idle.
///   generating — an active cleanup_transcript_text_cmd call is in flight.
///   error      — last health check or generation failed.
export type LlmStatusKind = "off" | "warming" | "ready" | "generating" | "error";

export interface LlmStatusState {
  kind: LlmStatusKind;
  /// Optional short phrase for the chip to surface (e.g. "Loading Qwen3 4B").
  detail: string | null;
}

export const llmStatus = $state<LlmStatusState>({ kind: "off", detail: null });

interface RefreshLlmStatusOptions {
  force?: boolean;
}

/// Poll the configured model's status once. Cheap enough to call on layout
/// mount, on recording start, and on Settings-panel open. Keeps the chip in
/// sync with loads / unloads that happen outside the frontend's observation
/// path (first-run, background reload).
///
/// Visibility rule: the chip appears only when there is something
/// meaningful for the user to know — the model is ready, generating, or
/// errored, or an explicit load is in flight (markLoading). It is hidden
/// when AI is off, no model is configured, or the configured model isn't
/// downloaded yet — those states surface in Settings, not as a stuck
/// "warming" pill that misleads the user into thinking the engine is busy.
export async function refreshLlmStatus(
  aiTier: string,
  llmModelId: string | null,
  options: RefreshLlmStatusOptions = {},
): Promise<void> {
  const force = options.force === true;

  // Generation is a foreground action with its own success/failure
  // transition. Ambient health checks should never hide it.
  if (llmStatus.kind === "generating") return;

  // Warming means a caller explicitly started a load. Ambient refreshes
  // should not clobber that in-flight signal; post-load callers pass
  // force=true to reconcile the final state.
  if (llmStatus.kind === "warming" && !force) return;

  if (aiTier === "off" || !hasTauriRuntime() || !llmModelId) {
    llmStatus.kind = "off";
    llmStatus.detail = null;
    return;
  }
  try {
    const status = await invoke<{ downloaded: boolean; loaded: boolean }>(
      "check_llm_model",
      { modelId: llmModelId },
    );
    if (status.loaded) {
      llmStatus.kind = "ready";
      llmStatus.detail = null;
    } else {
      // Not loaded and no load in flight — keep the chip hidden. A genuine
      // load (triggered from DictationPage / SettingsPage) will call
      // markLoading() to flip the chip.
      llmStatus.kind = "off";
      llmStatus.detail = null;
    }
  } catch (err) {
    llmStatus.kind = "error";
    llmStatus.detail = typeof err === "string" ? err : (err as Error)?.message ?? "Unknown error";
  }
}

export function markLoading(detail: string | null = "Loading model"): void {
  llmStatus.kind = "warming";
  llmStatus.detail = detail;
}

export function markError(detail: string | null = null): void {
  llmStatus.kind = "error";
  llmStatus.detail = detail;
}

export function markGenerating(detail: string | null = null): void {
  llmStatus.kind = "generating";
  llmStatus.detail = detail;
}

export function markGenerationDone(success: boolean, detail: string | null = null): void {
  if (success) {
    llmStatus.kind = "ready";
    llmStatus.detail = null;
  } else {
    llmStatus.kind = "error";
    llmStatus.detail = detail;
  }
}
