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

import { CartService } from './cart.service';
import { TenantService } from '../tenant.service';
import { Cart } from './cart.interface';

describe('CartService', () => {
  let service: CartService;
  let httpMock: HttpTestingController;
  let tenantServiceMock: any;

  const mockCart: Cart = {
    id: 123,
    tenant_codigo: 'acme',
    status: 'active',
    items: [
      {
        id: 1,
        cantidad: 2,
        precio_unitario: '10.00',
        product_id: 5,
        product_variant_id: 10,
        product: {
          nombre: 'Test Product (Size: M)',
          imagen: null
        }
      }
    ],
    subtotal: '20.00'
  };

  beforeEach(() => {
    tenantServiceMock = {
      getTenantApiUrl: vi.fn().mockReturnValue('http://api.test/tenants/acme'),
      tenant: vi.fn().mockReturnValue({ codigo: 'acme' })
    };

    TestBed.configureTestingModule({
      providers: [
        CartService,
        provideHttpClient(),
        provideHttpClientTesting(),
        { provide: TenantService, useValue: tenantServiceMock }
      ]
    });

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

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

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should initialize with null cart signal', () => {
    expect(service.cart()).toBeNull();
  });

  it('should load cart and update signal', () => {
    service.loadCart().subscribe((cart) => {
      expect(cart).toEqual(mockCart);
      expect(service.cart()).toEqual(mockCart);
    });

    const req = httpMock.expectOne('http://api.test/tenants/acme/cart');
    expect(req.request.method).toBe('GET');
    expect(req.request.withCredentials).toBe(true);
    req.flush({ data: mockCart });
  });

  it('should clear cart state locally', () => {
    service.clearCart();

    expect(service.cart()).toEqual({
      id: null,
      tenant_codigo: 'acme',
      status: 'active',
      items: [],
      subtotal: '0.00'
    });
  });

  it('should add item and update signal', () => {
    service.addItem(10, 2).subscribe((res) => {
      expect(res.data).toEqual(mockCart);
      expect(service.cart()).toEqual(mockCart);
    });

    const req = httpMock.expectOne('http://api.test/tenants/acme/cart/items');
    expect(req.request.method).toBe('POST');
    expect(req.request.body).toEqual({ product_variant_id: 10, cantidad: 2 });
    expect(req.request.withCredentials).toBe(true);
    req.flush({ data: mockCart });
  });

  it('should update item quantity and update signal', () => {
    const updatedCart = { ...mockCart, subtotal: '30.00' };
    updatedCart.items[0].cantidad = 3;

    service.updateItemQuantity(10, 3).subscribe((res) => {
      expect(res.data).toEqual(updatedCart);
      expect(service.cart()).toEqual(updatedCart);
    });

    const req = httpMock.expectOne('http://api.test/tenants/acme/cart/items/10');
    expect(req.request.method).toBe('PATCH');
    expect(req.request.body).toEqual({ cantidad: 3 });
    expect(req.request.withCredentials).toBe(true);
    req.flush({ data: updatedCart });
  });

  it('should remove item and update signal', () => {
    const emptyCart: Cart = {
      id: 123,
      tenant_codigo: 'acme',
      status: 'active',
      items: [],
      subtotal: '0.00'
    };

    service.removeItem(10).subscribe((res) => {
      expect(res.data).toEqual(emptyCart);
      expect(service.cart()).toEqual(emptyCart);
    });

    const req = httpMock.expectOne('http://api.test/tenants/acme/cart/items/10');
    expect(req.request.method).toBe('DELETE');
    expect(req.request.withCredentials).toBe(true);
    req.flush({ data: emptyCart });
  });
});
