import '@angular/compiler';
import { HttpClient, HttpContext, provideHttpClient, withInterceptors } from '@angular/common/http';
import {
  HttpTestingController,
  provideHttpClientTesting
} from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { globalLoadingInterceptor } from './global-loading.interceptor';
import { GlobalLoadingService } from './global-loading.service';
import { LOADING_MODE, LoadingMode } from './loading-mode';

describe('globalLoadingInterceptor', () => {
  let client: HttpClient;
  let httpTesting: HttpTestingController;
  let loadingService: GlobalLoadingService;

  beforeEach(() => {
    vi.useFakeTimers();
    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(withInterceptors([globalLoadingInterceptor])),
        provideHttpClientTesting()
      ]
    });

    client = TestBed.inject(HttpClient);
    httpTesting = TestBed.inject(HttpTestingController);
    loadingService = TestBed.inject(GlobalLoadingService);
  });

  afterEach(() => {
    httpTesting.verify();
    TestBed.resetTestingModule();
    vi.useRealTimers();
  });

  it('tracks concurrent requests until the last one completes', () => {
    client.get('/api/first').subscribe();
    client.get('/api/second').subscribe();
    vi.advanceTimersByTime(150);

    expect(loadingService.isLoading()).toBe(true);

    httpTesting.expectOne('/api/first').flush({});
    expect(loadingService.isLoading()).toBe(true);

    httpTesting.expectOne('/api/second').flush({});
    expect(loadingService.isLoading()).toBe(false);
  });

  it('stops loading when a request fails', () => {
    client.get('/api/failing').subscribe({ error: () => undefined });
    vi.advanceTimersByTime(150);

    expect(loadingService.isLoading()).toBe(true);

    httpTesting.expectOne('/api/failing').flush(
      { message: 'Error' },
      { status: 500, statusText: 'Server Error' }
    );

    expect(loadingService.isLoading()).toBe(false);
  });

  it.each<LoadingMode>(['custom', 'none'])(
    'does not show the global loader for %s requests',
    (loadingMode) => {
      const context = new HttpContext().set(LOADING_MODE, loadingMode);

      client.get(`/api/${loadingMode}`, { context }).subscribe();
      vi.advanceTimersByTime(150);

      expect(loadingService.isLoading()).toBe(false);

      httpTesting.expectOne(`/api/${loadingMode}`).flush({});
    }
  );
});
