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

// Mock refreshToken BEFORE importing apiFetch
vi.mock('@/lib/refreshToken', () => ({
  refreshToken: vi.fn(),
}));

import { apiFetch, type ApiFetchOptions } from '@/utils/apiFetch';
import { refreshToken } from '@/lib/refreshToken';

const OK_JSON = (data: any) => new Response(JSON.stringify(data), { status: 200, headers: { 'Content-Type': 'application/json' } });
const STATUS = (code: number) => new Response('', { status: code });

describe('apiFetch', () => {
  const originalFetch = global.fetch;

  beforeEach(() => {
    vi.restoreAllMocks();
    localStorage.clear();
  });

  afterEach(() => {
    global.fetch = originalFetch;
  });

  it('returns ok response and sends Authorization when token present', async () => {
    localStorage.setItem('auth_token', 'token-123');

    const fetchSpy = vi.fn(async (_input: any, init?: RequestInit) => {
      expect(init?.credentials).toBe('include');
      const auth = (init?.headers as any)?.Authorization || (init?.headers as any)?.authorization;
      expect(auth).toBe('Bearer token-123');
      return OK_JSON({ ok: true });
    }) as unknown as typeof fetch;

    global.fetch = fetchSpy;

    const res = await apiFetch('/endpoint');
    expect(res.ok).toBe(true);
    expect(fetchSpy).toHaveBeenCalledTimes(1);
  });

  it('refreshes token on 401 and retries successfully', async () => {
    localStorage.setItem('auth_token', 'old-token');
    (refreshToken as unknown as vi.Mock).mockResolvedValue('new-token');

    let call = 0;
    const fetchSpy = vi.fn(async (_input: any, init?: RequestInit) => {
      call++;
      if (call === 1) return STATUS(401); // initial
      // retried original request should carry new token
      const auth = (init?.headers as any)?.Authorization;
      expect(auth).toBe('Bearer new-token');
      return OK_JSON({ ok: true });
    }) as unknown as typeof fetch;

    global.fetch = fetchSpy;

    const res = await apiFetch('/secure');
    expect(res.ok).toBe(true);
    expect(localStorage.getItem('auth_token')).toBe('new-token');
    expect(fetchSpy).toHaveBeenCalledTimes(2);
  });

  it('logs out and throws when refresh fails', async () => {
    localStorage.setItem('auth_token', 'old-token');
    (refreshToken as unknown as vi.Mock).mockResolvedValue(null);

    const logout = vi.fn();

    global.fetch = vi.fn(async () => STATUS(401)) as unknown as typeof fetch;

    await expect(apiFetch('/secure', {} as ApiFetchOptions, logout)).rejects.toThrow();
    expect(logout).toHaveBeenCalledTimes(1);
    expect(localStorage.getItem('auth_token')).toBeNull();
  });
});
