import { InvalidPriceError, PriceNotFoundError } from '../domain/errors.js';
import type { PricingRepository, PricingService as PricingServicePort } from '../domain/ports.js';
import {
  calculateVatAmountCents,
  VAT_BASIS_POINTS,
  type PriceCalculation,
  type PriceCalculationCommand,
  type SetVariantPriceCommand,
  type VariantPrice,
} from '../domain/price.js';

export class PricingService implements PricingServicePort {
  constructor(private readonly repository: PricingRepository) {}

  async calculate(input: PriceCalculationCommand): Promise<PriceCalculation> {
    ensurePositiveInteger(input.quantity, 'Quantity must be a positive integer');
    const price = await this.repository.findByVariantId(input.variantId);
    if (!price) {
      throw new PriceNotFoundError();
    }

    const netSubtotalCents = price.netUnitAmountCents * input.quantity;
    const vatAmountCents = calculateVatAmountCents(netSubtotalCents, price.vatRate);
    return {
      variantId: input.variantId,
      quantity: input.quantity,
      currency: price.currency,
      vatRate: price.vatRate,
      vatBasisPoints: VAT_BASIS_POINTS[price.vatRate],
      netUnitAmountCents: price.netUnitAmountCents,
      netSubtotalCents,
      vatAmountCents,
      totalCents: netSubtotalCents + vatAmountCents,
    };
  }

  async getVariantPrice(variantId: string): Promise<VariantPrice | undefined> {
    return this.repository.findByVariantId(variantId);
  }

  async setVariantPrice(input: SetVariantPriceCommand): Promise<VariantPrice> {
    ensureNonNegativeInteger(input.netUnitAmountCents, 'Price must be a non-negative integer');
    return this.repository.setVariantPrice(input);
  }

  async seedVariantPrice(variantId: string): Promise<void> {
    await this.repository.seedVariantPrice(variantId);
  }
}

function ensurePositiveInteger(value: number, message: string): void {
  if (!Number.isInteger(value) || value <= 0) {
    throw new InvalidPriceError(message);
  }
}

function ensureNonNegativeInteger(value: number, message: string): void {
  if (!Number.isInteger(value) || value < 0) {
    throw new InvalidPriceError(message);
  }
}
