import { Transaction } from "../generated/kysely-tailordb";
import {
  CurrencyNotFoundError,
  InactiveCurrencyError,
  InvalidExchangeRateError,
  SameCurrencyPairError,
} from "../lib/errors.generated";
import { ok, err } from "@tailor-platform/erp-kit/module";

interface CreateExchangeRateInput {
  sourceCurrencyId: string;
  targetCurrencyId: string;
  rate: number;
  effectiveDate: Date;
}

/**
 * Function: createExchangeRate
 *
 * Establishes a new conversion ratio between a currency pair with a specific
 * effective date. The rate specifies how many units of the target currency
 * equal one unit of the source currency.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: CreateExchangeRateInput & CF,
) {
  const { sourceCurrencyId, targetCurrencyId, rate, effectiveDate, ...customFields } = input;

  // 1. Check source currency exists
  const sourceCurrency = await db
    .selectFrom("Currency")
    .selectAll()
    .where("id", "=", sourceCurrencyId)
    .executeTakeFirst();

  if (!sourceCurrency) {
    return err(new CurrencyNotFoundError(sourceCurrencyId));
  }

  // 2. Check source currency is active
  if (!sourceCurrency.isActive) {
    return err(new InactiveCurrencyError(sourceCurrency.code));
  }

  // 3. Check target currency exists
  const targetCurrency = await db
    .selectFrom("Currency")
    .selectAll()
    .where("id", "=", targetCurrencyId)
    .executeTakeFirst();

  if (!targetCurrency) {
    return err(new CurrencyNotFoundError(targetCurrencyId));
  }

  // 4. Check target currency is active
  if (!targetCurrency.isActive) {
    return err(new InactiveCurrencyError(targetCurrency.code));
  }

  // 5. Check source and target are different
  if (sourceCurrencyId === targetCurrencyId) {
    return err(new SameCurrencyPairError(sourceCurrencyId));
  }

  // 6. Validate rate is positive
  if (rate <= 0) {
    return err(new InvalidExchangeRateError(String(rate)));
  }

  // 7. Create exchange rate
  const exchangeRate = await db
    .insertInto("ExchangeRate")
    .values({
      ...(customFields as Record<string, unknown>),
      sourceCurrencyId,
      targetCurrencyId,
      rate,
      effectiveDate,
      createdAt: new Date(),
      updatedAt: null,
    })
    .returningAll()
    .executeTakeFirst();

  return ok({ exchangeRate: exchangeRate! });
}
