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

const getMock = vi.fn()
const postMock = vi.fn()
const refreshSessionBundleMock = vi.fn()

vi.mock('@/lib/http/client', () => ({
  get: getMock,
  post: postMock,
  refreshSessionBundle: refreshSessionBundleMock,
}))

describe('auth service', () => {
  beforeEach(() => {
    getMock.mockReset()
    postMock.mockReset()
    refreshSessionBundleMock.mockReset()
    postMock.mockResolvedValue(undefined)
    refreshSessionBundleMock.mockResolvedValue(undefined)
  })

  it('loads public auth capabilities without auth headers', async () => {
    const { getAuthCapabilities } = await import('./auth')

    await getAuthCapabilities()

    expect(getMock).toHaveBeenCalledWith('/auth/capabilities', undefined, { auth: false })
  })

  it('normalizes null oauth provider lists from auth capabilities', async () => {
    getMock.mockResolvedValue({
      password: true,
      email_activation: false,
      email_code: false,
      sms_code: false,
      password_reset: false,
      admin_bootstrap_required: undefined,
      oauth_providers: null,
    })

    const { getAuthCapabilities } = await import('./auth')
    const result = await getAuthCapabilities()

    expect(result.admin_bootstrap_required).toBe(false)
    expect(result.email_activation).toBe(false)
    expect(result.oauth_providers).toEqual([])
  })

  it('preserves admin bootstrap status from auth capabilities', async () => {
    getMock.mockResolvedValue({
      password: true,
      email_activation: true,
      email_code: false,
      sms_code: false,
      password_reset: false,
      admin_bootstrap_required: true,
      oauth_providers: [],
    })

    const { getAuthCapabilities } = await import('./auth')
    const result = await getAuthCapabilities()

    expect(result.admin_bootstrap_required).toBe(true)
    expect(result.email_activation).toBe(true)
  })

  it('requests oauth authorization url without auth headers', async () => {
    const { getOAuthAuthorizationUrl } = await import('./auth')

    await getOAuthAuthorizationUrl('github', 'https://admin.example.com/login/oauth/callback')

    expect(getMock).toHaveBeenCalledWith(
      '/auth/oauth/github',
      { return_to: 'https://admin.example.com/login/oauth/callback' },
      { auth: false },
    )
  })

  it('exchanges oauth handoff code without auth headers', async () => {
    const { exchangeOAuthHandoff } = await import('./auth')

    await exchangeOAuthHandoff('handoff-code')

    expect(postMock).toHaveBeenCalledWith(
      '/auth/oauth/exchange',
      { code: 'handoff-code' },
      { auth: false, credentials: 'include' },
    )
  })

  it('verifies password-login totp with the temporary challenge token', async () => {
    const { verifyTOTPAfterPasswordLogin } = await import('./auth')

    await verifyTOTPAfterPasswordLogin({
      user_id: 42,
      code: '123456',
      device_id: 'device-1',
      temp_token: 'temp-token-demo',
    })

    expect(postMock).toHaveBeenCalledWith(
      '/auth/login/totp-verify',
      {
        user_id: 42,
        code: '123456',
        device_id: 'device-1',
        temp_token: 'temp-token-demo',
      },
      { auth: false, credentials: 'include' },
    )
  })

  it('submits public registration without auth headers', async () => {
    const { register } = await import('./auth')

    await register({
      username: 'new-user',
      password: 'SecurePass123!',
      email: 'new-user@example.com',
      nickname: 'New User',
    })

    expect(postMock).toHaveBeenCalledWith(
      '/auth/register',
      {
        username: 'new-user',
        password: 'SecurePass123!',
        email: 'new-user@example.com',
        nickname: 'New User',
      },
      { auth: false },
    )
  })

  it('submits first-admin bootstrap with bootstrap secret header', async () => {
    const { bootstrapAdmin } = await import('./auth')

    await bootstrapAdmin({
      username: 'bootstrap_admin',
      password: 'Bootstrap123!@#',
      email: 'bootstrap_admin@example.com',
      nickname: 'Bootstrap Admin',
      bootstrap_secret: 'bootstrap-secret-demo',
    })

    expect(postMock).toHaveBeenCalledWith(
      '/auth/bootstrap-admin',
      {
        username: 'bootstrap_admin',
        password: 'Bootstrap123!@#',
        email: 'bootstrap_admin@example.com',
        nickname: 'Bootstrap Admin',
      },
      {
        auth: false,
        credentials: 'include',
        headers: {
          'X-Bootstrap-Secret': 'bootstrap-secret-demo',
        },
      },
    )
  })

  it('activates email accounts without auth headers', async () => {
    const { activateEmail } = await import('./auth')

    await activateEmail('activation-token')

    expect(postMock).toHaveBeenCalledWith(
      '/auth/activate-email',
      { token: 'activation-token' },
      { auth: false },
    )
  })

  it('resends activation emails without auth headers', async () => {
    const { resendActivationEmail } = await import('./auth')

    await resendActivationEmail({ email: 'new-user@example.com' })

    expect(postMock).toHaveBeenCalledWith(
      '/auth/resend-activation',
      { email: 'new-user@example.com' },
      { auth: false },
    )
  })

  it('sends sms purpose instead of the deprecated scene field', async () => {
    const { sendSmsCode } = await import('./auth')

    await sendSmsCode({
      phone: '13812345678',
      purpose: 'register',
    })

    expect(postMock).toHaveBeenCalledWith(
      '/auth/send-code',
      {
        phone: '13812345678',
        purpose: 'register',
      },
      { auth: false },
    )
  })

  it('sends refresh_token when logging out with a persisted session', async () => {
    const { logout } = await import('./auth')

    await logout('refresh-token-demo')

    expect(postMock).toHaveBeenCalledWith(
      '/auth/logout',
      {
        refresh_token: 'refresh-token-demo',
      },
      { credentials: 'include' },
    )
  })

  it('omits the request body when no refresh_token is available', async () => {
    const { logout } = await import('./auth')

    await logout()

    expect(postMock).toHaveBeenCalledWith('/auth/logout', undefined, { credentials: 'include' })
  })

  it('refreshes the session through the shared refresh single-flight when no body token is supplied', async () => {
    const { refreshSession } = await import('./auth')

    await refreshSession()

    expect(refreshSessionBundleMock).toHaveBeenCalledTimes(1)
    expect(postMock).not.toHaveBeenCalledWith(
      '/auth/refresh',
      undefined,
      { auth: false, credentials: 'include' },
    )
  })
})
