import { eq } from 'drizzle-orm';
import { accrueCommission } from '@platform-modules/affiliate';
import type { ReferralConfig, MultidealEnv } from '@/server/env.js';
import type { TxDrizzleClient } from '@/server/db/client.js';
import { referrals } from '@/server/db/schema.js';
import { asModuleMoneyTx } from '@/server/affiliate-module/host-retained.js';
import { insertOutboxRow } from '@/server/db/queries/outbox.js';
import { enqueueOutbox } from '@/server/queues/outbox-producer.js';
import { asReferralId } from '@/server/platform-seams/ids.js';
import type { ReferralEffect } from './effects.js';

export interface QualifiedResult {
  referrerUserId: string;
  rewardAgorot: number;
}

export interface ReferralEffectDeps {
  qualifyRefereeFirstPaid: (
    db: TxDrizzleClient,
    args: { refereeUserId: string; purchaseId: string; amountPaidAgorot: number },
  ) => Promise<QualifiedResult | null>;
  accrueAffiliateCommission: (
    db: TxDrizzleClient,
    cfg: ReferralConfig,
    args: {
      refereeUserId: string;
      purchaseId: string;
      amountPaidAgorot: number;
      orderLineId?: string;
    },
  ) => Promise<number>;
  clawbackForRefund?: (
    db: TxDrizzleClient,
    args: { purchaseId: string; refundEventId: string; refundFraction: number },
  ) => Promise<void>;
}

export interface ApplyEffectsContext {
  db: TxDrizzleClient;
  env: MultidealEnv;
  cfg: ReferralConfig;
  deps: ReferralEffectDeps;
}

export interface ApplyEffectsResult {
  qualified: QualifiedResult | null;
}

export async function applyEffects(
  ctx: ApplyEffectsContext,
  effects: ReferralEffect[],
): Promise<ApplyEffectsResult> {
  let qualified: QualifiedResult | null = null;

  for (const effect of effects) {
    switch (effect.kind) {
      case 'write-zero-commission-audit':
        await ctx.db.transaction(async (tx) => {
          await accrueCommission(asModuleMoneyTx(tx), {
            userId: effect.referrerUserId,
            amountMinor: 0n,
            entryType: 'affiliate_commission',
            sourceType: effect.sourceType,
            sourceId: effect.purchaseId,
            matureAt: effect.at,
            referralId: effect.referralId,
            memo: effect.memo,
          });
        });
        break;
      case 'set-quarantined-at':
        await ctx.db
          .update(referrals)
          .set({ quarantinedAt: effect.at })
          .where(eq(referrals.id, asReferralId(effect.referralId)));
        break;
      case 'qualify-referee-first-paid':
        qualified = await ctx.deps.qualifyRefereeFirstPaid(ctx.db, {
          refereeUserId: effect.refereeUserId,
          purchaseId: effect.purchaseId,
          amountPaidAgorot: effect.amountPaidAgorot,
        });
        break;
      case 'accrue-affiliate-commission':
        await ctx.deps.accrueAffiliateCommission(ctx.db, ctx.cfg, {
          refereeUserId: effect.refereeUserId,
          purchaseId: effect.purchaseId,
          amountPaidAgorot: effect.amountPaidAgorot,
          orderLineId: effect.orderLineId,
        });
        break;
      case 'enqueue-qualified-notification': {
        const { id: outboxId } = await insertOutboxRow(ctx.db, {
          aggregateType: 'referral',
          aggregateId: effect.purchaseId,
          eventType: 'referral.qualified',
          payload: {
            referrerUserId: effect.referrerUserId,
            refereeUserId: effect.refereeUserId,
            rewardAgorot: effect.rewardAgorot,
            purchaseId: effect.purchaseId,
          },
        });
        await enqueueOutbox(outboxId);
        break;
      }
      case 'execute-clawback':
        if (ctx.deps.clawbackForRefund) {
          await ctx.deps.clawbackForRefund(ctx.db, {
            purchaseId: effect.purchaseId,
            refundEventId: effect.refundEventId,
            refundFraction: effect.refundFraction,
          });
        }
        break;
    }
  }

  return { qualified };
}
