/**
 * Tauri-aware refresh token storage adapter.
 *
 * In Tauri mode the refresh token is written to BOTH the OS Keychain
 * (via the `store_refresh_token` / `load_refresh_token` /
 * `clear_refresh_token` Rust commands, see `src-tauri/src/auth.rs`)
 * AND `localStorage`.  The Keychain is the preferred read source;
 * `localStorage` is a defence-in-depth backup so a missing or broken
 * keychain command (e.g. the Rust side not yet wired up) never
 * silently loses the refresh token — which would force the user back
 * to /login on every reload.
 *
 * In plain Web mode (no Tauri runtime) only `localStorage` is used.
 *
 * Keychain failures are logged and transparently downgraded to
 * `localStorage` so a broken OS credential service never blocks login
 * (KTD-confirmed decision: silent localStorage fallback).
 */

const STORAGE_KEY = 'agentkit.refresh_token'

/** Detect whether the current page is running inside a Tauri WebView. */
export function isTauri(): boolean {
  return typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window
}

/**
 * Lazily import the Tauri `invoke` API.
 *
 * The dynamic import keeps the `@tauri-apps/api` bundle out of the Web
 * build when it is not needed and lets us `catch` import-time errors
 * that occur when the WebView is not actually Tauri-backed.
 */
async function tauriInvoke<T>(cmd: string, args?: Record<string, unknown>): Promise<T> {
  const mod = await import('@tauri-apps/api/core')
  return mod.invoke<T>(cmd, args)
}

function localGet(): string | null {
  try {
    return localStorage.getItem(STORAGE_KEY)
  } catch {
    return null
  }
}

function localSet(value: string): void {
  try {
    localStorage.setItem(STORAGE_KEY, value)
  } catch (e) {
    // Log the error so a silently-failed persist is visible in the console.
    // Previously this was swallowed, which meant a rotated refresh token
    // could fail to persist without any signal — the next reload would
    // find a stale (already-rotated) token and redirect to /login.
    console.error('[auth] localStorage.setItem failed — refresh token NOT persisted', e)
  }
}

function localRemove(): void {
  try {
    localStorage.removeItem(STORAGE_KEY)
  } catch {
    /* ignore */
  }
}

export const tauriAuthStorage = {
  /**
   * Persist the refresh token.
   *
   * Always writes `localStorage` first (the durable backup), then
   * attempts the OS Keychain.  The Keychain write is best-effort: if
   * the Rust command is not registered or rejects, the localStorage
   * copy still lets the next reload rehydrate the session.
   */
  async setRefreshToken(token: string): Promise<void> {
    localSet(token)
    if (isTauri()) {
      try {
        await tauriInvoke<void>('store_refresh_token', { token })
      } catch (e) {
        console.warn('[auth] Keychain write failed, localStorage backup used', e)
      }
    }
  },

  /**
   * Read the persisted refresh token.
   *
   * Tries the OS Keychain first; on any failure or empty result falls
   * back to `localStorage`.  Returns `null` when no token is stored
   * anywhere.
   */
  async getRefreshToken(): Promise<string | null> {
    if (isTauri()) {
      try {
        const value = await tauriInvoke<string | null>('load_refresh_token')
        if (value) return value
      } catch (e) {
        console.warn('[auth] Keychain read failed, falling back to localStorage', e)
      }
    }
    return localGet()
  },

  /**
   * Remove the persisted refresh token.
   *
   * Always clears `localStorage`; the Keychain clear is best-effort.
   */
  async clearRefreshToken(): Promise<void> {
    localRemove()
    if (isTauri()) {
      try {
        await tauriInvoke<void>('clear_refresh_token')
      } catch (e) {
        console.warn('[auth] Keychain clear failed, localStorage already cleared', e)
      }
    }
  },
}

export type TauriAuthStorage = typeof tauriAuthStorage
