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

import { accessApi } from './access'

describe('accessApi', () => {
  beforeEach(() => {
    vi.restoreAllMocks()

    const store = new Map<string, string>()
    vi.stubGlobal('localStorage', {
      getItem: (key: string) => store.get(key) ?? null,
      setItem: (key: string, value: string) => {
        store.set(key, value)
      },
      removeItem: (key: string) => {
        store.delete(key)
      },
    })
    vi.stubGlobal('window', {
      localStorage: globalThis.localStorage,
      location: { origin: 'https://explorer.example.org' },
      dispatchEvent: vi.fn(),
      addEventListener: vi.fn(),
      removeEventListener: vi.fn(),
    })
  })

  it('stores the session token on login', async () => {
    vi.stubGlobal(
      'fetch',
      vi.fn().mockResolvedValue({
        ok: true,
        json: async () => ({
          user: {
            id: 'user-1',
            email: 'ops@example.org',
            username: 'ops',
          },
          token: 'jwt-token',
          expires_at: '2026-04-16T00:00:00Z',
        }),
      }),
    )

    const result = await accessApi.login('ops@example.org', 'secret-password')

    expect(result.token).toBe('jwt-token')
    expect(accessApi.getStoredAccessToken()).toBe('jwt-token')
  })

  it('sends scope, expiry, and quota fields when creating an API key', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({
        api_key: 'ek_test',
      }),
    })
    vi.stubGlobal('fetch', fetchMock)
    window.localStorage.setItem('explorer_access_token', 'jwt-token')

    await accessApi.createAPIKey({
      name: 'Thirdweb key',
      tier: 'pro',
      productSlug: 'thirdweb-rpc',
      expiresDays: 30,
      monthlyQuota: 250000,
      scopes: ['rpc:read', 'rpc:write'],
    })

    const [, init] = fetchMock.mock.calls[0]
    expect(init?.method).toBe('POST')
    expect(init?.headers).toBeTruthy()
    expect((init?.headers as Headers).get('Authorization')).toBe('Bearer jwt-token')
    expect(JSON.parse(String(init?.body))).toEqual({
      name: 'Thirdweb key',
      tier: 'pro',
      product_slug: 'thirdweb-rpc',
      expires_days: 30,
      monthly_quota: 250000,
      scopes: ['rpc:read', 'rpc:write'],
    })
  })

  it('requests admin audit with limit and product filters', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({
        entries: [],
      }),
    })
    vi.stubGlobal('fetch', fetchMock)
    window.localStorage.setItem('explorer_access_token', 'jwt-token')

    await accessApi.listAdminAudit(50, 'thirdweb-rpc')

    const [url, init] = fetchMock.mock.calls[0]
    expect(String(url)).toContain('/explorer-api/v1/access/admin/audit?limit=50&product=thirdweb-rpc')
    expect((init?.headers as Headers).get('Authorization')).toBe('Bearer jwt-token')
  })

  it('requests user audit with the selected entry limit', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({
        entries: [],
      }),
    })
    vi.stubGlobal('fetch', fetchMock)
    window.localStorage.setItem('explorer_access_token', 'jwt-token')

    await accessApi.listAudit(10)

    const [url, init] = fetchMock.mock.calls[0]
    expect(String(url)).toContain('/explorer-api/v1/access/audit?limit=10')
    expect((init?.headers as Headers).get('Authorization')).toBe('Bearer jwt-token')
  })

  it('requests admin subscriptions with the selected status filter', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({
        subscriptions: [],
      }),
    })
    vi.stubGlobal('fetch', fetchMock)
    window.localStorage.setItem('explorer_access_token', 'jwt-token')

    await accessApi.listAdminSubscriptions('suspended')

    const [url, init] = fetchMock.mock.calls[0]
    expect(String(url)).toContain('/explorer-api/v1/access/admin/subscriptions?status=suspended')
    expect((init?.headers as Headers).get('Authorization')).toBe('Bearer jwt-token')
  })

  it('creates a wallet nonce and stores the returned wallet session', async () => {
    const fetchMock = vi
      .fn()
      .mockResolvedValueOnce({
        ok: true,
        json: async () => ({
          nonce: 'nonce-123',
          address: '0x4A666F96fC8764181194447A7dFdb7d471b301C8',
        }),
      })
      .mockResolvedValueOnce({
        ok: true,
        json: async () => ({
          token: 'wallet-jwt',
          expires_at: '2026-04-16T00:00:00Z',
          track: 'wallet',
          permissions: ['access'],
        }),
      })

    vi.stubGlobal('fetch', fetchMock)

    const nonceResponse = await accessApi.createWalletNonce('0x4A666F96fC8764181194447A7dFdb7d471b301C8')
    const session = await accessApi.authenticateWallet(
      '0x4A666F96fC8764181194447A7dFdb7d471b301C8',
      '0xsigned',
      nonceResponse.nonce,
    )

    expect(String(fetchMock.mock.calls[0][0])).toContain('/explorer-api/v1/auth/nonce')
    expect(String(fetchMock.mock.calls[1][0])).toContain('/explorer-api/v1/auth/wallet')
    expect(session.token).toBe('wallet-jwt')
    expect(accessApi.getStoredWalletSession()?.address).toBe('0x4A666F96fC8764181194447A7dFdb7d471b301C8')
    expect(accessApi.getStoredAccessToken()).toBe('wallet-jwt')
  })
})
