import { PLATFORM_ID, TransferState } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';

import { environment } from '../../../../environments/environment';
import { AuthService } from './auth.service';
import { CookieService } from '../cookie/cookie.service';

describe('AuthService', () => {
  let cookieStore: Record<string, string> = {};

  beforeEach(() => {
    cookieStore = {};
    TestBed.resetTestingModule();
  });

  function createCookieServiceStub() {
    return {
      get: (name: string) => cookieStore[name] || null,
      set: (name: string, value: string) => { cookieStore[name] = value; },
      delete: (name: string) => { delete cookieStore[name]; }
    };
  }

  it('stores token and user on successful login', () => {
    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(),
        provideHttpClientTesting(),
        AuthService,
        { provide: CookieService, useValue: createCookieServiceStub() },
        TransferState
      ]
    });

    const service = TestBed.inject(AuthService);
    const httpController = TestBed.inject(HttpTestingController);

    service.login({ email: 'ada@example.com', password: 'secret123' }).subscribe((user) => {
      expect(user.email).toBe('ada@example.com');
    });

    const request = httpController.expectOne(`${environment.url}login`);
    expect(request.request.method).toBe('POST');
    request.flush({
      message: 'Sesion iniciada correctamente.',
      token: 'plain-text-token',
      token_type: 'Bearer',
      user: {
        id: 1,
        nombre_apellido: 'Ada Lovelace',
        email: 'ada@example.com'
      }
    });

    expect(service.token()).toBe('plain-text-token');
    expect(service.user()?.email).toBe('ada@example.com');
    expect(service.isAuthenticated()).toBe(true);
    expect(cookieStore['shopit.auth.token']).toBe('plain-text-token');

    httpController.verify();
  });

  it('hydrates token from cookies and loads the current user during bootstrap', async () => {
    cookieStore['shopit.auth.token'] = 'persisted-token';

    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(),
        provideHttpClientTesting(),
        AuthService,
        { provide: CookieService, useValue: createCookieServiceStub() },
        TransferState
      ]
    });

    const service = TestBed.inject(AuthService);
    const httpController = TestBed.inject(HttpTestingController);

    const bootstrapPromise = service.bootstrap();
    const request = httpController.expectOne(`${environment.url}me`);

    expect(request.request.method).toBe('GET');
    request.flush({
      id: 9,
      nombre_apellido: 'Grace Hopper',
      email: 'grace@example.com'
    });

    await expect(bootstrapPromise).resolves.toBeUndefined();
    expect(service.user()?.email).toBe('grace@example.com');
    expect(service.isAuthenticated()).toBe(true);

    httpController.verify();
  });

  it('clears the session when bootstrap receives 401 from /me', async () => {
    cookieStore['shopit.auth.token'] = 'expired-token';

    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(),
        provideHttpClientTesting(),
        AuthService,
        { provide: CookieService, useValue: createCookieServiceStub() },
        TransferState
      ]
    });

    const service = TestBed.inject(AuthService);
    const httpController = TestBed.inject(HttpTestingController);

    const bootstrapPromise = service.bootstrap();
    const request = httpController.expectOne(`${environment.url}me`);

    request.flush({ message: 'Unauthorized' }, { status: 401, statusText: 'Unauthorized' });

    await expect(bootstrapPromise).resolves.toBeUndefined();
    expect(service.user()).toBeNull();
    expect(service.token()).toBeNull();
    expect(service.isAuthenticated()).toBe(false);
    expect(cookieStore['shopit.auth.token']).toBeUndefined();

    httpController.verify();
  });

  it('registers without creating a session', () => {
    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(),
        provideHttpClientTesting(),
        AuthService,
        { provide: CookieService, useValue: createCookieServiceStub() },
        TransferState
      ]
    });

    const service = TestBed.inject(AuthService);
    const httpController = TestBed.inject(HttpTestingController);

    service
      .register({
        nombre_apellido: 'Ada Lovelace',
        email: 'ada@example.com',
        password: 'secret123',
        password_confirmation: 'secret123'
      })
      .subscribe((response) => {
        expect(response.data.email).toBe('ada@example.com');
      });

    const request = httpController.expectOne(`${environment.url}register`);
    expect(request.request.method).toBe('POST');
    request.flush({
      message: 'Usuario registrado correctamente.',
      data: {
        id: 1,
        nombre_apellido: 'Ada Lovelace',
        email: 'ada@example.com'
      }
    });

    expect(service.token()).toBeNull();
    expect(service.user()).toBeNull();
    expect(service.isAuthenticated()).toBe(false);

    httpController.verify();
  });

  it('clears local session even when logout request fails', () => {
    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(),
        provideHttpClientTesting(),
        AuthService,
        { provide: CookieService, useValue: createCookieServiceStub() },
        TransferState
      ]
    });

    const service = TestBed.inject(AuthService);
    const httpController = TestBed.inject(HttpTestingController);

    service.login({ email: 'ada@example.com', password: 'secret123' }).subscribe();
    httpController.expectOne(`${environment.url}login`).flush({
      message: 'Sesion iniciada correctamente.',
      token: 'plain-text-token',
      token_type: 'Bearer',
      user: {
        id: 1,
        nombre_apellido: 'Ada Lovelace',
        email: 'ada@example.com'
      }
    });

    service.logout().subscribe({
      error: () => undefined
    });

    const logoutRequest = httpController.expectOne(`${environment.url}logout`);
    expect(logoutRequest.request.method).toBe('POST');
    logoutRequest.flush({ message: 'Unauthorized' }, { status: 401, statusText: 'Unauthorized' });

    expect(service.token()).toBeNull();
    expect(service.user()).toBeNull();
    expect(cookieStore['shopit.auth.token']).toBeUndefined();

    httpController.verify();
  });
});
