/**
 * Unit tests for the tauri-auth adapter.
 *
 * The adapter is a thin wrapper over the Tauri `invoke` API and
 * `localStorage`.  We mock `@tauri-apps/api/core` so we can assert
 * which command is dispatched and simulate the two relevant
 * failure modes (Keychain unavailable, invoke throws).
 */

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'

// Mock the @tauri-apps/api/core module BEFORE importing the SUT.
const mockInvoke = vi.fn()
vi.mock('@tauri-apps/api/core', () => ({
  invoke: (...args: unknown[]) => mockInvoke(...args),
}))

// Import after the mock is in place.
const { isTauri, tauriAuthStorage } = await import('@/api/tauri-auth')

const STORAGE_KEY = 'agentkit.refresh_token'

/**
 * Polyfill a minimal in-memory `localStorage` for the test environment.
 *
 * Some happy-dom versions ship a stub that lacks `.clear()` (a known
 * regression in vitest 2.x with happy-dom).  We work around it by
 * providing our own implementation that has the full Storage interface.
 */
const _store = new Map<string, string>()
const _localStoragePolyfill: Storage = {
  get length() {
    return _store.size
  },
  clear() {
    _store.clear()
  },
  getItem(key: string) {
    return _store.has(key) ? (_store.get(key) as string) : null
  },
  key(index: number) {
    return Array.from(_store.keys())[index] ?? null
  },
  removeItem(key: string) {
    _store.delete(key)
  },
  setItem(key: string, value: string) {
    _store.set(key, String(value))
  },
}

beforeEach(() => {
  ;(globalThis as { localStorage?: Storage }).localStorage = _localStoragePolyfill
  mockInvoke.mockReset()
  _store.clear()
  setTauri(false)
})

afterEach(() => {
  setTauri(false)
  _store.clear()
  vi.restoreAllMocks()
})

/** Inject / remove the Tauri runtime marker on `window`. */
function setTauri(present: boolean): void {
  if (present) {
    ;(window as unknown as { __TAURI_INTERNALS__?: object }).__TAURI_INTERNALS__ = {}
  } else {
    delete (window as unknown as { __TAURI_INTERNALS__?: object }).__TAURI_INTERNALS__
  }
}

beforeEach(() => {
  mockInvoke.mockReset()
  localStorage.clear()
  setTauri(false)
})

afterEach(() => {
  setTauri(false)
  localStorage.clear()
  vi.restoreAllMocks()
})

describe('isTauri', () => {
  it('returns true when __TAURI_INTERNALS__ is present on window', () => {
    setTauri(true)
    expect(isTauri()).toBe(true)
  })

  it('returns false when __TAURI_INTERNALS__ is missing', () => {
    setTauri(false)
    expect(isTauri()).toBe(false)
  })
})

describe('tauriAuthStorage.setRefreshToken', () => {
  it('writes to localStorage in Web mode (no Tauri)', async () => {
    await tauriAuthStorage.setRefreshToken('web-token')
    expect(localStorage.getItem(STORAGE_KEY)).toBe('web-token')
    expect(mockInvoke).not.toHaveBeenCalled()
  })

  it('calls invoke("store_refresh_token") in Tauri mode', async () => {
    setTauri(true)
    mockInvoke.mockResolvedValueOnce(undefined)
    await tauriAuthStorage.setRefreshToken('keychain-token')
    expect(mockInvoke).toHaveBeenCalledTimes(1)
    expect(mockInvoke).toHaveBeenCalledWith('store_refresh_token', { token: 'keychain-token' })
    // Web fallback should NOT have been used.
    expect(localStorage.getItem(STORAGE_KEY)).toBeNull()
  })

  it('falls back to localStorage when invoke throws in Tauri mode', async () => {
    setTauri(true)
    mockInvoke.mockRejectedValueOnce(new Error('keychain unavailable'))
    const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined)

    await tauriAuthStorage.setRefreshToken('fallback-token')

    expect(warnSpy).toHaveBeenCalled()
    expect(localStorage.getItem(STORAGE_KEY)).toBe('fallback-token')
  })
})

describe('tauriAuthStorage.getRefreshToken', () => {
  it('returns null from localStorage in Web mode when nothing is stored', async () => {
    const result = await tauriAuthStorage.getRefreshToken()
    expect(result).toBeNull()
    expect(mockInvoke).not.toHaveBeenCalled()
  })

  it('returns the value from localStorage in Web mode', async () => {
    localStorage.setItem(STORAGE_KEY, 'stored-web')
    const result = await tauriAuthStorage.getRefreshToken()
    expect(result).toBe('stored-web')
    expect(mockInvoke).not.toHaveBeenCalled()
  })

  it('returns the value from invoke("load_refresh_token") in Tauri mode', async () => {
    setTauri(true)
    mockInvoke.mockResolvedValueOnce('keychain-value')
    const result = await tauriAuthStorage.getRefreshToken()
    expect(result).toBe('keychain-value')
    expect(mockInvoke).toHaveBeenCalledTimes(1)
    // The first argument is the command name; the second may be undefined
    // when no args are needed.
    expect(mockInvoke.mock.calls[0]?.[0]).toBe('load_refresh_token')
  })

  it('normalizes a null invoke response to null in Tauri mode', async () => {
    setTauri(true)
    mockInvoke.mockResolvedValueOnce(null)
    const result = await tauriAuthStorage.getRefreshToken()
    expect(result).toBeNull()
  })

  it('falls back to localStorage when invoke throws in Tauri mode', async () => {
    setTauri(true)
    localStorage.setItem(STORAGE_KEY, 'web-copy')
    mockInvoke.mockRejectedValueOnce(new Error('read failed'))
    const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined)

    const result = await tauriAuthStorage.getRefreshToken()

    expect(warnSpy).toHaveBeenCalled()
    expect(result).toBe('web-copy')
  })
})

describe('tauriAuthStorage.clearRefreshToken', () => {
  it('removes the entry from localStorage in Web mode', async () => {
    localStorage.setItem(STORAGE_KEY, 'to-remove')
    await tauriAuthStorage.clearRefreshToken()
    expect(localStorage.getItem(STORAGE_KEY)).toBeNull()
    expect(mockInvoke).not.toHaveBeenCalled()
  })

  it('calls invoke("clear_refresh_token") in Tauri mode', async () => {
    setTauri(true)
    mockInvoke.mockResolvedValueOnce(undefined)
    await tauriAuthStorage.clearRefreshToken()
    expect(mockInvoke).toHaveBeenCalledTimes(1)
    expect(mockInvoke.mock.calls[0]?.[0]).toBe('clear_refresh_token')
  })

  it('still removes the localStorage copy when invoke throws in Tauri mode', async () => {
    setTauri(true)
    localStorage.setItem(STORAGE_KEY, 'still-here')
    mockInvoke.mockRejectedValueOnce(new Error('clear failed'))
    const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined)

    await tauriAuthStorage.clearRefreshToken()

    expect(warnSpy).toHaveBeenCalled()
    expect(localStorage.getItem(STORAGE_KEY)).toBeNull()
  })
})
