import { jest, describe, test, expect, beforeEach, afterEach } from '@jest/globals';
import { createMockLogger, createMockConfig } from '../../__test-utils__/mockFactories.js';

// We need to mock global fetch and setInterval/setTimeout
const { default: BenchmarkService } = await import('../benchmarkService.js');

describe('BenchmarkService', () => {
  let service;
  let logger;
  let config;
  let originalFetch;

  beforeEach(() => {
    jest.useFakeTimers();
    logger = createMockLogger();
    config = { backend: { baseUrl: 'https://test-backend.example.com' } };
    service = new BenchmarkService(config, logger);
    originalFetch = global.fetch;
    global.fetch = jest.fn();
  });

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

  test('constructor sets backend URL from config', () => {
    expect(service.azureBackendUrl).toBe('https://test-backend.example.com');
  });

  test('constructor uses default URL when config has no backend', () => {
    const s = new BenchmarkService({}, logger);
    expect(s.azureBackendUrl).toContain('azurefd.net');
  });

  test('getBenchmarks returns null and warns when no data', () => {
    const result = service.getBenchmarks();
    expect(result).toBeNull();
    expect(logger.warn).toHaveBeenCalled();
  });

  test('getBenchmarks returns benchmark text when available', () => {
    service.benchmarkText = 'some benchmark data';
    expect(service.getBenchmarks()).toBe('some benchmark data');
  });

  test('getBenchmarkTable is alias for getBenchmarks', () => {
    service.benchmarkText = 'table data';
    expect(service.getBenchmarkTable()).toBe('table data');
  });

  test('loadBenchmarks skips if already loading', async () => {
    service.isLoading = true;
    await service.loadBenchmarks();
    expect(logger.debug).toHaveBeenCalledWith('Benchmark loading already in progress');
  });

  test('loadBenchmarks skips if endpoint not available', async () => {
    service.endpointAvailable = false;
    await service.loadBenchmarks();
    expect(global.fetch).not.toHaveBeenCalled();
  });

  test('loadBenchmarks succeeds with valid response', async () => {
    global.fetch.mockResolvedValueOnce({
      ok: true,
      text: jest.fn().mockResolvedValueOnce('benchmark text data')
    });

    await service.loadBenchmarks();
    expect(service.benchmarkText).toBe('benchmark text data');
    expect(service.lastUpdated).toBeInstanceOf(Date);
    expect(service.isLoading).toBe(false);
  });

  test('loadBenchmarks handles 404 by disabling endpoint', async () => {
    global.fetch.mockResolvedValueOnce({
      ok: false,
      status: 404,
      statusText: 'Not Found'
    });

    await service.loadBenchmarks();
    expect(service.endpointAvailable).toBe(false);
    expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('404'));
  });

  test('loadBenchmarks handles non-404 error', async () => {
    global.fetch.mockResolvedValueOnce({
      ok: false,
      status: 500,
      statusText: 'Internal Server Error'
    });

    await service.loadBenchmarks();
    expect(service.endpointAvailable).toBe(true);
    expect(logger.error).toHaveBeenCalled();
  });

  test('loadBenchmarks handles empty response', async () => {
    global.fetch.mockResolvedValueOnce({
      ok: true,
      text: jest.fn().mockResolvedValueOnce('')
    });

    await service.loadBenchmarks();
    expect(service.benchmarkText).toBeNull();
    expect(service.isLoading).toBe(false);
  });

  test('loadBenchmarks handles fetch timeout (AbortError)', async () => {
    const abortError = new Error('Aborted');
    abortError.name = 'AbortError';
    global.fetch.mockRejectedValueOnce(abortError);

    await service.loadBenchmarks();
    expect(logger.error).toHaveBeenCalled();
    expect(service.isLoading).toBe(false);
  });

  test('loadBenchmarks includes auth header when apiKey configured', async () => {
    service.config = { apiKey: 'test-api-key' };
    global.fetch.mockResolvedValueOnce({
      ok: true,
      text: jest.fn().mockResolvedValueOnce('data')
    });

    await service.loadBenchmarks();
    const fetchCall = global.fetch.mock.calls[0];
    expect(fetchCall[1].headers.Authorization).toBe('Bearer test-api-key');
  });

  test('needsRefresh returns true when never updated', () => {
    expect(service.needsRefresh()).toBe(true);
  });

  test('needsRefresh returns false shortly after update', () => {
    service.lastUpdated = new Date();
    expect(service.needsRefresh()).toBe(false);
  });

  test('initialize loads benchmarks and schedules refresh', async () => {
    global.fetch.mockResolvedValueOnce({
      ok: true,
      text: jest.fn().mockResolvedValueOnce('initial data')
    });

    await service.initialize();
    expect(service.benchmarkText).toBe('initial data');
    expect(logger.info).toHaveBeenCalledWith('Benchmark service initialized');
  });

  test('initialize handles errors', async () => {
    global.fetch.mockRejectedValueOnce(new Error('network fail'));
    await service.initialize();
    expect(service.benchmarkText).toBeNull();
    expect(logger.error).toHaveBeenCalled();
  });

  test('forceRefresh calls loadBenchmarks', async () => {
    global.fetch.mockResolvedValueOnce({
      ok: true,
      text: jest.fn().mockResolvedValueOnce('refreshed')
    });
    await service.forceRefresh();
    expect(service.benchmarkText).toBe('refreshed');
  });

  test('getStatus returns service status', () => {
    service.benchmarkText = 'data';
    service.lastUpdated = new Date('2024-01-01');
    const status = service.getStatus();
    expect(status.initialized).toBe(true);
    expect(status.hasData).toBe(true);
    expect(status.lastUpdated).toBe('2024-01-01T00:00:00.000Z');
    expect(status.isLoading).toBe(false);
  });

  test('getStatus when no data', () => {
    const status = service.getStatus();
    expect(status.initialized).toBe(false);
    expect(status.lastUpdated).toBeNull();
  });
});
