import { provideHttpClient } 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 { environment } from '../../../environments/environment';
import { CatalogAvailabilityService } from './catalog/catalog-availability.service';
import { CheckoutService } from './checkout.service';

describe('CheckoutService', () => {
  let service: CheckoutService;
  let httpMock: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [CheckoutService, provideHttpClient(), provideHttpClientTesting()],
    });

    service = TestBed.inject(CheckoutService);
    httpMock = TestBed.inject(HttpTestingController);
  });

  afterEach(() => httpMock.verify());

  it('starts one direct checkout with all selected variants', async () => {
    const payload = {
      direct_items: [
        { catalog_item_id: 10, variant_id: 101, cantidad: 1 },
        { catalog_item_id: 10, variant_id: 102, cantidad: 1 },
      ],
    };
    const purchasePromise = service.startCheckout('desfile', payload);
    const request = httpMock.expectOne(`${environment.url}tenants/desfile/compras/start-checkout`);

    expect(request.request.method).toBe('POST');
    expect(request.request.body).toEqual(payload);

    request.flush({ data: { id: 55 } });

    await expect(purchasePromise).resolves.toMatchObject({ id: 55 });
  });

  it('refreshes catalog availability when starting checkout fails', async () => {
    const catalogAvailabilityService = TestBed.inject(CatalogAvailabilityService);
    const notifyAvailabilityChanged = vi.spyOn(
      catalogAvailabilityService,
      'notifyAvailabilityChanged',
    );
    const purchasePromise = service.startCheckout('desfile', { cart_id: 12 });
    const request = httpMock.expectOne(`${environment.url}tenants/desfile/compras/start-checkout`);

    request.flush(
      { message: 'No hay stock disponible.' },
      { status: 422, statusText: 'Unprocessable Entity' },
    );

    await expect(purchasePromise).rejects.toBeTruthy();
    expect(notifyAvailabilityChanged).toHaveBeenCalledOnce();
  });

  it('waits for the expired cart refresh before refreshing catalog availability', async () => {
    const catalogAvailabilityService = TestBed.inject(CatalogAvailabilityService);
    const notifyAvailabilityChanged = vi.spyOn(
      catalogAvailabilityService,
      'notifyAvailabilityChanged',
    );
    const purchasePromise = service.startCheckout('desfile', { cart_id: 12 });
    const request = httpMock.expectOne(`${environment.url}tenants/desfile/compras/start-checkout`);

    request.flush(
      {
        code: 'stock_reservation.expired',
        message: 'La reserva de stock venció.',
      },
      { status: 422, statusText: 'Unprocessable Entity' },
    );

    await expect(purchasePromise).rejects.toBeTruthy();
    expect(notifyAvailabilityChanged).not.toHaveBeenCalled();
  });

  it('preserves checkout timing fields when completing a purchase', async () => {
    const purchasePromise = service.completePurchase('desfile', 55);
    const request = httpMock.expectOne(`${environment.url}tenants/desfile/compras/55/complete`);
    const response = {
      status: 'pending_payment',
      expires_at: '2026-08-26T20:00:00.000Z',
      expires_in_seconds: 900,
      server_time: '2026-08-26T19:45:00.000Z',
    };

    expect(request.request.method).toBe('POST');
    request.flush({ data: response });

    await expect(purchasePromise).resolves.toEqual(response);
  });
});
