import { describe, expect, it } from 'vitest';
import { InventoryService } from '../application/inventory-service.js';
import {
  InsufficientReservedStockError,
  InsufficientStockError,
  InvalidStockQuantityError,
} from '../domain/errors.js';
import type { InventoryRepository } from '../domain/ports.js';
import type { SetAvailableStockCommand, StockCommand, StockItem } from '../domain/stock.js';

const STORE_ID = '00000000-0000-0000-0000-000000000001';

const STOCK: StockItem = {
  id: 'stock-1',
  variantId: 'variant-1',
  storeId: STORE_ID,
  available: 1,
  reserved: 0,
  sold: 0,
  incoming: 0,
  createdAt: new Date('2026-01-01T00:00:00Z'),
  updatedAt: new Date('2026-01-01T00:00:00Z'),
};

function repository(overrides: Partial<InventoryRepository> = {}): InventoryRepository {
  return {
    findByVariantId: async () => STOCK,
    setAvailable: async (_input: SetAvailableStockCommand) => STOCK,
    reserve: async (_input: StockCommand) => STOCK,
    release: async (_input: StockCommand) => STOCK,
    confirm: async (_input: StockCommand) => STOCK,
    ...overrides,
  };
}

describe('InventoryService', () => {
  it('rejects non-positive reserve quantities before persistence', async () => {
    const service = new InventoryService(repository());

    await expect(
      service.reserve({ variantId: 'variant-1', storeId: STORE_ID, quantity: 0 }),
    ).rejects.toBeInstanceOf(InvalidStockQuantityError);
  });

  it('maps failed reservation to insufficient stock', async () => {
    const service = new InventoryService(repository({ reserve: async () => undefined }));

    await expect(
      service.reserve({ variantId: 'variant-1', storeId: STORE_ID, quantity: 1 }),
    ).rejects.toBeInstanceOf(InsufficientStockError);
  });

  it('maps failed release to insufficient reserved stock', async () => {
    const service = new InventoryService(repository({ release: async () => undefined }));

    await expect(
      service.release({ variantId: 'variant-1', storeId: STORE_ID, quantity: 1 }),
    ).rejects.toBeInstanceOf(InsufficientReservedStockError);
  });

  it('checks availability without mutating stock', async () => {
    const service = new InventoryService(repository());

    await expect(service.checkAvailability('variant-1', STORE_ID, 1)).resolves.toEqual({
      available: true,
      availableQuantity: 1,
    });
    await expect(service.checkAvailability('variant-1', STORE_ID, 2)).resolves.toEqual({
      available: false,
      availableQuantity: 1,
    });
  });
});
