import type { TxDrizzleClient } from '@/server/db/client.js';
import { decide as decidePromo } from '@/server/domain/promo/machine.js';
import { applyEffects as applyPromoEffects } from '@/server/domain/promo/apply-effects.js';
import { getPromoUsageCounts } from '@/server/promo/promotions-platform.js';
import { asPromoId, asUserId } from '@/server/platform-seams/ids.js';
import * as promoQueries from '@/server/db/queries/promo-codes.js';
import { runPromoOrchestration, buildValidateCtx } from '@/server/promo/service.js';
import type {
  DiscountBreakdown,
  PromoRejectReason,
  PromoCode,
  ResolvedCart,
} from '@/server/promo/types.js';

export type ReservePromoResult =
  | { ok: true; code: PromoCode; breakdown: DiscountBreakdown }
  | { ok: false; reason: PromoRejectReason };

export async function reservePromo(
  db: TxDrizzleClient,
  codeStr: string,
  user: { id: string; isClubMember: boolean },
  cart: ResolvedCart,
  platformFeePct?: number,
): Promise<ReservePromoResult> {
  const code = await promoQueries.getPromoCodeByCode(db, codeStr);
  if (!code) return { ok: false, reason: 'NOT_FOUND' };

  const usage = await getPromoUsageCounts(db, code.id, user.id);
  const preCtx = await buildValidateCtx(db, user, cart, usage.userUses);
  if (platformFeePct != null) preCtx.platformFeePct = platformFeePct;
  const preliminary = runPromoOrchestration({ ...code, redemptionCount: usage.globalUses }, preCtx);
  if (!preliminary.ok) return preliminary;

  const decision = decidePromo(
    { usageReserved: false, redemptionFinalized: false },
    {
      kind: 'promo_applied',
      promoCodeId: asPromoId(code.id),
      userId: asUserId(user.id),
    },
  );
  if (!decision.ok) return { ok: false, reason: 'OUT_OF_QUOTA' };

  const applied = await applyPromoEffects(
    {
      db,
      reserve: {
        ids: {
          promoCodeId: asPromoId(code.id),
          userId: asUserId(user.id),
        },
        code,
        user: { id: asUserId(user.id), isClubMember: user.isClubMember },
        cart,
        platformFeePct,
        revalidateLockedUsage: async ({ globalUses, userUses }) => {
          const lockedCtx = await buildValidateCtx(db, user, cart, userUses);
          if (platformFeePct != null) lockedCtx.platformFeePct = platformFeePct;
          const lockedCode = { ...code, redemptionCount: globalUses };
          const result = runPromoOrchestration(lockedCode, lockedCtx);
          return result.ok ? { ok: true, code: lockedCode, breakdown: result.breakdown } : result;
        },
      },
    },
    decision.effects,
  );

  if (!applied.ok) return applied;
  if (!applied.reservedPromo) return { ok: false, reason: 'OUT_OF_QUOTA' };
  return {
    ok: true,
    code: applied.reservedPromo.code,
    breakdown: applied.reservedPromo.breakdown,
  };
}
