import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { computed, signal } from '@angular/core';
import { TestBed } from '@angular/core/testing';

import { environment } from '../../../../environments/environment';
import { AuthService } from './auth.service';
import { authInterceptor } from './auth.interceptor';

function createAuthServiceStub(token: string | null = null) {
  const tokenState = signal(token);

  return {
    token: tokenState.asReadonly(),
    isAuthenticated: computed(() => tokenState() !== null),
    clearSession: vi.fn(() => tokenState.set(null))
  };
}

describe('authInterceptor', () => {
  beforeEach(() => {
    TestBed.resetTestingModule();
  });

  it('adds the bearer token when a session exists', () => {
    const authService = createAuthServiceStub('jwt-token');

    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(withInterceptors([authInterceptor])),
        provideHttpClientTesting(),
        { provide: AuthService, useValue: authService }
      ]
    });

    const client = TestBed.inject(HttpClient);
    const httpController = TestBed.inject(HttpTestingController);

    client.get(`${environment.url}me`).subscribe();

    const request = httpController.expectOne(`${environment.url}me`);
    expect(request.request.headers.get('Authorization')).toBe('Bearer jwt-token');
    request.flush({ id: 1 });

    httpController.verify();
  });

  it('does not add the authorization header when no token exists', () => {
    const authService = createAuthServiceStub();

    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(withInterceptors([authInterceptor])),
        provideHttpClientTesting(),
        { provide: AuthService, useValue: authService }
      ]
    });

    const client = TestBed.inject(HttpClient);
    const httpController = TestBed.inject(HttpTestingController);

    client.get(`${environment.url}productos`).subscribe();

    const request = httpController.expectOne(`${environment.url}productos`);
    expect(request.request.headers.has('Authorization')).toBe(false);
    request.flush([]);

    httpController.verify();
  });

  it('propagates a 401 without clearing the local session', () => {
    const authService = createAuthServiceStub('expired-token');

    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(withInterceptors([authInterceptor])),
        provideHttpClientTesting(),
        { provide: AuthService, useValue: authService }
      ]
    });

    const client = TestBed.inject(HttpClient);
    const httpController = TestBed.inject(HttpTestingController);

    let receivedError: unknown;
    let receivedStatus: number | null = null;
    client.get(`${environment.url}me`).subscribe({
      error: (error: HttpErrorResponse) => {
        receivedError = error;
        receivedStatus = error.status;
      }
    });

    const request = httpController.expectOne(`${environment.url}me`);
    request.flush({ message: 'Unauthorized' }, { status: 401, statusText: 'Unauthorized' });

    expect(receivedError).toBeInstanceOf(HttpErrorResponse);
    expect(receivedStatus).toBe(401);
    expect(authService.clearSession).not.toHaveBeenCalled();
    httpController.verify();
  });
});
