import { describe, it, expect, beforeEach } from 'vitest'
import { http, HttpResponse } from 'msw'
import { server } from '@/test/mocks/server'
import api from '@/services/api'

describe('api (Axios instance)', () => {
  beforeEach(() => {
    localStorage.clear()
  })

  it('attaches Authorization header when access_token exists in localStorage', async () => {
    localStorage.setItem('access_token', 'test-token-123')

    let capturedAuth: string | undefined
    server.use(
      http.get('/api/auth/me', ({ request }) => {
        capturedAuth = request.headers.get('Authorization') ?? undefined
        return HttpResponse.json({ id: 1 })
      }),
    )

    await api.get('/auth/me')
    expect(capturedAuth).toBe('Bearer test-token-123')
  })

  it('does NOT attach Authorization header when no token in localStorage', async () => {
    let capturedAuth: string | null = null
    server.use(
      http.get('/api/auth/me', ({ request }) => {
        capturedAuth = request.headers.get('Authorization')
        return HttpResponse.json({ id: 1 })
      }),
    )

    await api.get('/auth/me')
    expect(capturedAuth).toBeNull()
  })

  it('on 401, refreshes token and retries the request', async () => {
    localStorage.setItem('access_token', 'expired-token')
    localStorage.setItem('refresh_token', 'valid-refresh-token')

    let callCount = 0
    server.use(
      http.get('/api/auth/me', ({ request }) => {
        callCount++
        const auth = request.headers.get('Authorization')
        if (auth === 'Bearer refreshed-access-token') {
          return HttpResponse.json({ id: 1, username: 'admin' })
        }
        return new HttpResponse(null, { status: 401 })
      }),
      http.post('/api/auth/refresh', () => {
        return HttpResponse.json({
          access_token: 'refreshed-access-token',
          refresh_token: 'refreshed-refresh-token',
          token_type: 'bearer',
        })
      }),
    )

    const response = await api.get('/auth/me')
    expect(response.data).toEqual({ id: 1, username: 'admin' })
    expect(callCount).toBe(2) // First call 401, then retried with new token
    expect(localStorage.getItem('access_token')).toBe('refreshed-access-token')
  })

  it('when refresh also fails, clears tokens', async () => {
    localStorage.setItem('access_token', 'expired-token')
    localStorage.setItem('refresh_token', 'expired-refresh-token')

    let refreshCalled = false
    server.use(
      http.get('/api/auth/me', () => {
        return new HttpResponse(null, { status: 401 })
      }),
      http.post('/api/auth/refresh', () => {
        refreshCalled = true
        return HttpResponse.json(
          { detail: 'Refresh token expired' },
          { status: 401 },
        )
      }),
    )

    await expect(api.get('/auth/me')).rejects.toThrow()
    expect(refreshCalled).toBe(true)
    expect(localStorage.getItem('access_token')).toBeNull()
    expect(localStorage.getItem('refresh_token')).toBeNull()
  })

  it('passes through non-401 errors without attempting refresh', async () => {
    localStorage.setItem('access_token', 'valid-token')
    localStorage.setItem('refresh_token', 'valid-refresh-token')

    let refreshCalled = false
    server.use(
      http.get('/api/auth/me', () => {
        return new HttpResponse(JSON.stringify({ detail: 'Server error' }), {
          status: 500,
        })
      }),
      http.post('/api/auth/refresh', () => {
        refreshCalled = true
        return HttpResponse.json({ access_token: 'new-token' })
      }),
    )

    await expect(api.get('/auth/me')).rejects.toThrow()
    expect(refreshCalled).toBe(false)
    // Tokens should NOT be cleared on 500
    expect(localStorage.getItem('access_token')).toBe('valid-token')
  })

  it('does not retry 401 more than once (prevents infinite loop)', async () => {
    localStorage.setItem('access_token', 'expired-token')
    localStorage.setItem('refresh_token', 'some-refresh-token')

    let meCallCount = 0
    server.use(
      http.get('/api/auth/me', () => {
        meCallCount++
        return new HttpResponse(null, { status: 401 })
      }),
      http.post('/api/auth/refresh', () => {
        // Refresh succeeds but the new token still gets 401
        return HttpResponse.json({
          access_token: 'still-invalid-token',
          refresh_token: 'new-refresh-token',
          token_type: 'bearer',
        })
      }),
    )

    await expect(api.get('/auth/me')).rejects.toThrow()
    // First call returns 401, triggers refresh (succeeds), retry also returns 401.
    // The retry has _retry=true so it does NOT trigger another refresh — just rejects.
    expect(meCallCount).toBe(2)
  })
})
