import { describe, expect, it } from 'vitest';
import { PromotionServiceImpl } from '../application/promotion-service.js';
import { PromotionInvalidError } from '../domain/errors.js';
import type { PromotionRepository } from '../domain/ports.js';
import type { NewPromotion, Promotion } from '../domain/promotion.js';

const PROMO: Promotion = {
  code: 'SAVE10',
  type: 'percent',
  value: 1000,
  startsAt: new Date('2026-01-01T00:00:00Z'),
  endsAt: new Date('2026-12-31T00:00:00Z'),
  usageLimit: null,
  usageCount: 0,
  active: true,
  createdAt: new Date('2026-01-01T00:00:00Z'),
  updatedAt: new Date('2026-01-01T00:00:00Z'),
};

function repo(promotion: Promotion = PROMO): PromotionRepository {
  return {
    findByCode: async () => promotion,
    create: async (input: NewPromotion) => ({
      ...promotion,
      ...input,
      usageLimit: input.usageLimit ?? null,
      usageCount: 0,
      active: input.active ?? true,
    }),
    findAll: async () => [promotion],
    update: async (_code: string, _patch: Partial<NewPromotion>) => promotion,
    delete: async (_code: string) => {},
  };
}

describe('PromotionService', () => {
  it('calculates percent and fixed discounts capped by cart total', async () => {
    await expect(
      new PromotionServiceImpl(repo()).calculateDiscount(
        'save10',
        2000,
        new Date('2026-06-01T00:00:00Z'),
      ),
    ).resolves.toMatchObject({ code: 'SAVE10', discountCents: 200 });
    await expect(
      new PromotionServiceImpl(
        repo({ ...PROMO, type: 'fixed_amount', value: 5000 }),
      ).calculateDiscount('x', 1200, new Date('2026-06-01T00:00:00Z')),
    ).resolves.toMatchObject({ discountCents: 1200 });
  });

  it('rejects expired or exhausted promotions', async () => {
    await expect(
      new PromotionServiceImpl(repo()).validateCode('save10', new Date('2027-01-01T00:00:00Z')),
    ).rejects.toBeInstanceOf(PromotionInvalidError);
    await expect(
      new PromotionServiceImpl(repo({ ...PROMO, usageLimit: 1, usageCount: 1 })).validateCode(
        'save10',
        new Date('2026-06-01T00:00:00Z'),
      ),
    ).rejects.toBeInstanceOf(PromotionInvalidError);
  });
});
