import { TestBed } from '@angular/core/testing';
import { CartService } from './cart.service';
import { LocalStorageService } from '../core/storage/local-storage.service';
import { CartItem, DeliveryOption } from '../models';

/**
 * cart.service.ts had no spec before this. It feeds totalWithDelivery and
 * allRequiredDeliveriesSelected directly into cart.component.ts's checkout
 * gate (isCheckoutDisabled) and into the offers/qty payload sent to
 * POST /api/v2/storefront/checkout - the exact contract F14-F16 rewired
 * this session. A regression here would silently let checkout proceed
 * with missing delivery selections, or compute the wrong cart total.
 */

function cartItem(overrides: Partial<CartItem> = {}): CartItem {
  return {
    categoryID: 1,
    itemID: 1,
    name: 'Widget',
    photos: null,
    description: '',
    currency: 'RUB',
    price: 1000,
    discount: 0,
    rating: 0,
    callbacks: null,
    questions: null,
    quantity: 1,
    ...overrides,
  } as CartItem;
}

function delivery(overrides: Partial<DeliveryOption> = {}): DeliveryOption {
  return { deliveryPrice: 0, deliveryPlace: 'Standard', deliveryTime: '3-5 days', ...overrides };
}

function createService(seedItems: CartItem[] = []): CartService {
  const storageStub = {
    getItem: (key: string) => (key === 'marketplace_cart' ? JSON.stringify(seedItems) : null),
    setItem: () => {},
    getJSON: () => null,
    setJSON: () => {},
  };
  TestBed.configureTestingModule({ providers: [{ provide: LocalStorageService, useValue: storageStub }] });
  return TestBed.inject(CartService);
}

describe('CartService', () => {
  describe('totalPrice', () => {
    it('sums price times quantity across items', () => {
      const service = createService([cartItem({ itemID: 1, price: 100, quantity: 2 }), cartItem({ itemID: 2, price: 50, quantity: 3 })]);

      expect(service.totalPrice()).toBe(350);
    });

    it('applies the item discount before multiplying by quantity', () => {
      const service = createService([cartItem({ price: 100, discount: 10, quantity: 2 })]);

      // getDiscountedPrice: 100 * (1 - 0.10) = 90, times qty 2 = 180
      expect(service.totalPrice()).toBe(180);
    });

    it('is 0 for an empty cart', () => {
      const service = createService([]);

      expect(service.totalPrice()).toBe(0);
    });
  });

  describe('totalDeliveryPrice / totalWithDelivery', () => {
    it('adds selectedDelivery price times quantity', () => {
      const service = createService([cartItem({ price: 100, quantity: 2, selectedDelivery: delivery({ deliveryPrice: 20 }) })]);

      expect(service.totalDeliveryPrice()).toBe(40);
      expect(service.totalWithDelivery()).toBe(240);
    });

    it('items with no selectedDelivery contribute 0 delivery cost', () => {
      const service = createService([cartItem({ price: 100, quantity: 1 })]);

      expect(service.totalDeliveryPrice()).toBe(0);
    });
  });

  describe('allRequiredDeliveriesSelected', () => {
    it('is true when no item requires a delivery selection', () => {
      const service = createService([cartItem({ deliveryOptions: [] })]);

      expect(service.allRequiredDeliveriesSelected()).toBeTrue();
    });

    it('is false when an item has delivery options but none selected', () => {
      const service = createService([cartItem({ deliveryOptions: [delivery()], selectedDelivery: undefined })]);

      expect(service.allRequiredDeliveriesSelected()).toBeFalse();
    });

    it('is true once a required delivery has been selected', () => {
      const service = createService([cartItem({ deliveryOptions: [delivery()], selectedDelivery: delivery() })]);

      expect(service.allRequiredDeliveriesSelected()).toBeTrue();
    });

    it('digital items never require a delivery selection, regardless of deliveryOptions', () => {
      const service = createService([cartItem({ deliveryMode: 'digital', deliveryOptions: [delivery()] })]);

      expect(service.allRequiredDeliveriesSelected()).toBeTrue();
    });

    it('deliverySelectionRequired: false overrides having deliveryOptions present', () => {
      const service = createService([cartItem({ deliveryOptions: [delivery()], deliverySelectionRequired: false })]);

      expect(service.allRequiredDeliveriesSelected()).toBeTrue();
    });

    it('this is the exact gate cart.component.ts checks before allowing checkout', () => {
      const service = createService([cartItem({ deliveryOptions: [delivery()] })]);

      // Mirrors CartComponent.isCheckoutDisabled's third condition.
      expect(!service.allRequiredDeliveriesSelected()).toBeTrue();
    });
  });

  describe('itemCount', () => {
    it('sums quantities, not the number of distinct lines', () => {
      const service = createService([cartItem({ itemID: 1, quantity: 2 }), cartItem({ itemID: 2, quantity: 3 })]);

      expect(service.itemCount()).toBe(5);
    });
  });

  describe('updateQuantity', () => {
    it('updates the quantity of the matching line', () => {
      const service = createService([cartItem({ itemID: 1, quantity: 1 })]);

      service.updateQuantity(1, 5);

      expect(service.items()[0].quantity).toBe(5);
    });

    it('removes the line entirely when quantity drops to 0, rather than storing a zero-quantity line', () => {
      const service = createService([cartItem({ itemID: 1, quantity: 1 })]);

      service.updateQuantity(1, 0);

      expect(service.items().length).toBe(0);
    });
  });

  describe('setSelectedDelivery', () => {
    it('sets the delivery option on the matching item only', () => {
      const service = createService([cartItem({ itemID: 1 }), cartItem({ itemID: 2 })]);

      service.setSelectedDelivery(1, delivery({ deliveryPlace: 'Express' }));

      expect(service.items().find(i => i.itemID === 1)?.selectedDelivery?.deliveryPlace).toBe('Express');
      expect(service.items().find(i => i.itemID === 2)?.selectedDelivery).toBeFalsy();
    });
  });

  describe('removeItem / removeItems', () => {
    it('removeItem drops the matching line by itemID when no variant is given', () => {
      const service = createService([cartItem({ itemID: 1 }), cartItem({ itemID: 2 })]);

      service.removeItem(1);

      expect(service.items().map(i => i.itemID)).toEqual([2]);
    });

    it('removeItems drops every listed id in one call', () => {
      const service = createService([cartItem({ itemID: 1 }), cartItem({ itemID: 2 }), cartItem({ itemID: 3 })]);

      service.removeItems([1, 3]);

      expect(service.items().map(i => i.itemID)).toEqual([2]);
    });
  });

  describe('clearCart', () => {
    it('empties the cart', () => {
      const service = createService([cartItem()]);

      service.clearCart();

      expect(service.items().length).toBe(0);
    });
  });

  describe('addItem - existing line fast path', () => {
    it('increases quantity synchronously when the item is already in the cart, without an API call', async () => {
      const service = createService([cartItem({ itemID: 1, quantity: 1 })]);

      await service.addItem(1, 2);

      expect(service.items()[0].quantity).toBe(3);
    });
  });
});
