// EN: Unit tests for @goodgo/http-client — HttpClient wrapper around Axios.
// VI: Unit tests cho @goodgo/http-client — wrapper HttpClient xung quanh Axios.
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { HttpClient, createHttpClient } from './index';

// ---------------------------------------------------------------------------
// Mock Axios (use vi.hoisted so the instance is available for vi.mock factory)
// ---------------------------------------------------------------------------
const { mockAxiosInstance, mockAxiosCreate } = vi.hoisted(() => {
  const interceptors = {
    request: { use: vi.fn() },
    response: { use: vi.fn() },
  };
  const instance = {
    interceptors,
    get: vi.fn(),
    post: vi.fn(),
    put: vi.fn(),
    patch: vi.fn(),
    delete: vi.fn(),
    defaults: { headers: {} },
  };
  return { mockAxiosInstance: instance, mockAxiosCreate: vi.fn(() => instance) };
});

vi.mock('axios', () => ({
  default: {
    create: mockAxiosCreate,
  },
}));

// ---------------------------------------------------------------------------
// createHttpClient factory
// ---------------------------------------------------------------------------
describe('createHttpClient', () => {
  it('should return an HttpClient instance', () => {
    const client = createHttpClient({ baseURL: 'http://localhost:8080' });
    expect(client).toBeInstanceOf(HttpClient);
  });
});

// ---------------------------------------------------------------------------
// HttpClient constructor
// ---------------------------------------------------------------------------
describe('HttpClient constructor', () => {
  it('should call axios.create with baseURL and timeout', () => {
    createHttpClient({ baseURL: 'http://api.goodgo.vn', timeout: 5000 });
    expect(mockAxiosCreate).toHaveBeenCalledWith(
      expect.objectContaining({
        baseURL: 'http://api.goodgo.vn',
        timeout: 5000,
      })
    );
  });

  it('should use default timeout of 30000 when not provided', () => {
    createHttpClient({ baseURL: 'http://api.goodgo.vn' });
    expect(mockAxiosCreate).toHaveBeenCalledWith(
      expect.objectContaining({ timeout: 30000 })
    );
  });

  it('should setup request and response interceptors', () => {
    createHttpClient({ baseURL: 'http://api.goodgo.vn' });
    expect(mockAxiosInstance.interceptors.request.use).toHaveBeenCalled();
    expect(mockAxiosInstance.interceptors.response.use).toHaveBeenCalled();
  });
});

// ---------------------------------------------------------------------------
// HTTP method delegates
// ---------------------------------------------------------------------------
describe('HttpClient HTTP methods', () => {
  let client: HttpClient;

  beforeEach(() => {
    vi.clearAllMocks();
    client = createHttpClient({ baseURL: 'http://api.goodgo.vn' });
  });

  it('get() delegates to axios instance GET', async () => {
    mockAxiosInstance.get.mockResolvedValueOnce({
      data: { success: true, data: { id: 1 }, timestamp: '2026-01-01T00:00:00Z' },
    });
    const result = await client.get('/api/v1/products');
    expect(mockAxiosInstance.get).toHaveBeenCalledWith('/api/v1/products', undefined);
    expect(result.success).toBe(true);
  });

  it('post() delegates to axios instance POST with body', async () => {
    const body = { name: 'Espresso', price: 35000 };
    mockAxiosInstance.post.mockResolvedValueOnce({
      data: { success: true, data: { id: 99 }, timestamp: '2026-01-01T00:00:00Z' },
    });
    const result = await client.post('/api/v1/products', body);
    expect(mockAxiosInstance.post).toHaveBeenCalledWith('/api/v1/products', body, undefined);
    expect(result.success).toBe(true);
  });

  it('put() delegates to axios instance PUT', async () => {
    mockAxiosInstance.put.mockResolvedValueOnce({
      data: { success: true, timestamp: '2026-01-01T00:00:00Z' },
    });
    await client.put('/api/v1/products/1', { price: 40000 });
    expect(mockAxiosInstance.put).toHaveBeenCalled();
  });

  it('patch() delegates to axios instance PATCH', async () => {
    mockAxiosInstance.patch.mockResolvedValueOnce({
      data: { success: true, timestamp: '2026-01-01T00:00:00Z' },
    });
    await client.patch('/api/v1/products/1', { price: 45000 });
    expect(mockAxiosInstance.patch).toHaveBeenCalled();
  });

  it('delete() delegates to axios instance DELETE', async () => {
    mockAxiosInstance.delete.mockResolvedValueOnce({
      data: { success: true, timestamp: '2026-01-01T00:00:00Z' },
    });
    await client.delete('/api/v1/products/1');
    expect(mockAxiosInstance.delete).toHaveBeenCalledWith('/api/v1/products/1', undefined);
  });
});

// ---------------------------------------------------------------------------
// setAuthToken / removeAuthToken (browser environment guard)
// ---------------------------------------------------------------------------
describe('HttpClient auth token management', () => {
  let client: HttpClient;

  beforeEach(() => {
    client = createHttpClient({ baseURL: 'http://api.goodgo.vn' });
  });

  it('setAuthToken does not throw in non-browser environment', () => {
    // EN: In Node/vitest environment window is not defined — should be a no-op.
    // VI: Trong môi trường Node/vitest window không tồn tại — nên là no-op.
    expect(() => client.setAuthToken('tok_abc')).not.toThrow();
  });

  it('removeAuthToken does not throw in non-browser environment', () => {
    expect(() => client.removeAuthToken()).not.toThrow();
  });
});
