import type { NewPromotion, Promotion, PromotionDiscount } from '../domain/promotion.js';
import { PromotionInvalidError } from '../domain/errors.js';
import type { PromotionRepository, PromotionService } from '../domain/ports.js';

function isActiveAt(promotion: Promotion, now: Date): boolean {
  return promotion.active && now >= promotion.startsAt && now <= promotion.endsAt;
}

export class PromotionServiceImpl implements PromotionService {
  constructor(private readonly promotions: PromotionRepository) {}

  async create(input: NewPromotion): Promise<Promotion> {
    return this.promotions.create(input);
  }

  async list(): Promise<Promotion[]> {
    return this.promotions.findAll();
  }

  async update(code: string, patch: Partial<NewPromotion>): Promise<Promotion> {
    return this.promotions.update(code, patch);
  }

  async delete(code: string): Promise<void> {
    return this.promotions.delete(code);
  }

  async calculateDiscount(
    code: string,
    cartTotalCents: number,
    now = new Date(),
  ): Promise<PromotionDiscount> {
    const promotion = await this.promotions.findByCode(code);
    if (!promotion || !isActiveAt(promotion, now)) {
      throw new Error('Promotion not found or inactive');
    }

    const discountCents =
      promotion.type === 'percent'
        ? Math.round((cartTotalCents * promotion.value) / 10000)
        : Math.min(promotion.value, cartTotalCents);

    return { code: promotion.code, type: promotion.type, discountCents };
  }

  async validateCode(code: string, now = new Date()): Promise<Promotion> {
    const promotion = await this.promotions.findByCode(code);
    if (!promotion) {
      throw new PromotionInvalidError('Promotion not found');
    }
    if (!isActiveAt(promotion, now)) {
      throw new PromotionInvalidError('Promotion not active');
    }
    if (promotion.usageLimit != null && promotion.usageCount >= promotion.usageLimit) {
      throw new PromotionInvalidError('Promotion usage limit reached');
    }
    return promotion;
  }
}
