import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import { apiClientMock } from '../../test-utils/mocks/client';

vi.mock('$lib/api/client', () => apiClientMock());

vi.mock('$lib/query/client', () => ({
  queryClient: { clear: vi.fn() }
}));

import { api } from '$lib/api/client';
import { queryClient } from '$lib/query/client';
import { bootstrap, login, logout, user } from './store.svelte';

beforeEach(() => {
  vi.resetAllMocks();
});

afterEach(() => {
  // Force-clear the store between tests by stubbing api.get to reject,
  // then calling bootstrap.
});

describe('auth store', () => {
  test('bootstrap populates user on 200', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValue({
      id: '1', username: 'alice', is_admin: false
    });
    await bootstrap();
    expect(user.value).toEqual({ id: '1', username: 'alice', is_admin: false });
  });

  test('bootstrap leaves user null on failure', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockRejectedValue(
      { code: 'unauthorized', message: 'no session', status: 401 }
    );
    await bootstrap();
    expect(user.value).toBeNull();
  });

  test('login sets user from LoginResponse.user', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValue({
      token: 't', user: { id: '2', username: 'bob', is_admin: true }
    });
    await login('bob', 'pw');
    expect(user.value).toEqual({ id: '2', username: 'bob', is_admin: true });
    expect(api.post).toHaveBeenCalledWith('/api/auth/login', {
      username: 'bob', password: 'pw'
    });
  });

  test('logout clears user, calls /api/auth/logout, and clears query cache', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValue(null);
    await logout();
    expect(user.value).toBeNull();
    expect(api.post).toHaveBeenCalledWith('/api/auth/logout', {});
    expect(queryClient.clear).toHaveBeenCalledTimes(1);
  });

  test('logout with silent:true skips the POST but still clears state', async () => {
    await logout({ silent: true });
    expect(user.value).toBeNull();
    expect(api.post).not.toHaveBeenCalled();
    expect(queryClient.clear).toHaveBeenCalledTimes(1);
  });

  test('logout swallows POST errors (best-effort)', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockRejectedValue(
      { code: 'server_error', message: 'boom', status: 500 }
    );
    await expect(logout()).resolves.toBeUndefined();
    expect(user.value).toBeNull();
  });
});
