import type { ToastItem, ToastSeverity } from "$lib/types/app";
import { errorMessage } from "$lib/utils/errors.js";

// Minimal toast store. Roll-our-own (no svelte-french-toast etc) so Lumotia
// stays lean. Toasts auto-dismiss after `duration` ms unless duration is 0
// (sticky) or unless the user clicks the close button.
//
// Severity → colour:
//   info    → moss      (informational, default 4s)
//   success → moss      (operation succeeded, default 3s)
//   warn    → signal    (degraded but not failed, default 6s)
//   error   → ember     (operation failed, sticky until dismissed)
//
// Usage from a component:
//   import { toasts } from '$lib/stores/toasts.svelte.js';
//   toasts.error('Could not start recording', 'Selected microphone disappeared.');
//   toasts.success('Saved');

let nextId = 1;

// Soft cap on how many toasts can be queued at once. Error toasts are sticky
// (duration: 0) so a misbehaving command that fires errors in a loop can pile
// up indefinitely without this cap. When exceeded, the oldest toast is
// evicted to make room.
const MAX_TOASTS = 50;

function defaultDuration(severity: ToastSeverity): number {
  switch (severity) {
    case "success": return 3000;
    case "info": return 4000;
    case "warn": return 6000;
    case "error": return 0; // sticky
    default: return 4000;
  }
}

function createToastsStore() {
  // $state requires a class field or top-level let in Svelte 5; we expose
  // `items` via a getter on the singleton.
  let items = $state<ToastItem[]>([]);

  function show(severity: ToastSeverity, title: string, body?: string) {
    const id = nextId++;
    const duration = defaultDuration(severity);
    const toast = { id, severity, title, body: body ?? '', duration };
    items.push(toast);
    if (items.length > MAX_TOASTS) {
      items.splice(0, items.length - MAX_TOASTS);
    }
    if (duration > 0) {
      setTimeout(() => dismiss(id), duration);
    }
    return id;
  }

  function dismiss(id: number) {
    const ix = items.findIndex(t => t.id === id);
    if (ix >= 0) items.splice(ix, 1);
  }

  function dismissAll() {
    items.length = 0;
  }

  return {
    get items() { return items; },
    info: (title: string, body?: string) => show("info", title, body),
    success: (title: string, body?: string) => show("success", title, body),
    warn: (title: string, body?: string) => show("warn", title, body),
    error: (title: string, body?: string) => show("error", title, body),
    dismiss,
    dismissAll,
  };
}

export const toasts = createToastsStore();

// Helper: wrap a Tauri invoke and toast on failure. Returns the result on
// success, throws on failure (so callers that need to handle the error
// can still try/catch).
//
//   import { invoke } from '@tauri-apps/api/core';
//   import { invokeWithToast } from '$lib/stores/toasts.svelte.js';
//   const result = await invokeWithToast('start_native_capture', { deviceName });
//
// Optional `errorTitle` overrides the default ("Action failed").
export async function invokeWithToast<TArgs, TResult>(
  invokeFn: (command: string, args?: TArgs) => Promise<TResult>,
  command: string,
  args?: TArgs,
  errorTitle?: string,
): Promise<TResult> {
  try {
    return await invokeFn(command, args);
  } catch (err) {
    toasts.error(errorTitle ?? "Action failed", errorMessage(err));
    throw err;
  }
}
