import { createDbService } from '@/server/services/db.js';
import { z } from 'zod';
import { eq } from 'drizzle-orm';
import * as purchaseQueries from '../../../db/queries/purchases.js';
import { paymentMethods, referrals as referralsTable } from '../../../db/schema.js';
import { ReferralConfigSchema } from '../../../env.js';
import { amountPaidAgorot } from '../../refund.js';
import type { OutboxHandler } from '../types.js';
import type { MultidealEnv } from '../../../env.js';
import { asOrderLineId, asReferralId, asUserId, toModuleRef } from '../../../platform-seams/ids.js';

const payloadSchema = z.looseObject({
  purchaseId: z.uuid(),
});

type Payload = z.infer<typeof payloadSchema>;

export const referralEarn: OutboxHandler<Payload> = {
  type: 'referral.earn',
  payloadSchema,
  async handle(ctx, payload, event) {
    const db = createDbService({ DATABASE_URL: ctx.DATABASE_URL });
    const purchase = await purchaseQueries.findById(db, payload.purchaseId);
    if (!purchase) {
      throw new Error(`[outbox] purchase not found: ${payload.purchaseId}`);
    }
    if (purchase.paymentStatus !== 'paid') {
      console.warn(
        JSON.stringify({
          event: 'referral_earn_skip_non_completed',
          purchaseId: payload.purchaseId,
          outboxId: event.id,
        }),
      );
      return;
    }
    if (!purchase.userId) {
      return;
    }

    const cfg = ReferralConfigSchema.parse(ctx);
    const [refRow] = await db
      .select({
        id: referralsTable.id,
        status: referralsTable.status,
        referrerUserId: referralsTable.referrerUserId,
        quarantinedAt: referralsTable.quarantinedAt,
        visitorIdReferee: referralsTable.visitorIdReferee,
        visitorIdReferrer: referralsTable.visitorIdReferrer,
      })
      .from(referralsTable)
      .where(eq(referralsTable.refereeUserId, purchase.userId))
      .limit(1);
    if (!refRow || refRow.quarantinedAt) {
      return;
    }

    const orderLineId = toModuleRef(asOrderLineId(purchase.id));
    const refereeUserId = toModuleRef(asUserId(purchase.userId));
    const referrerUserId = toModuleRef(asUserId(refRow.referrerUserId));
    const referralId = toModuleRef(asReferralId(refRow.id));
    const [refereeCard, referrerCard] = await Promise.all([
      db
        .select({ cardFingerprint: paymentMethods.cardFingerprint })
        .from(paymentMethods)
        .where(eq(paymentMethods.userId, refereeUserId))
        .limit(1),
      db
        .select({ cardFingerprint: paymentMethods.cardFingerprint })
        .from(paymentMethods)
        .where(eq(paymentMethods.userId, referrerUserId))
        .limit(1),
    ]);

    await import('@/server/referrals/fraud/register-all.js');
    const [
      { runFraudPipeline },
      { getReferralSettings },
      { qualifyRefereeFirstPaid, accrueAffiliateCommission },
      { applyEffects },
      { asReferralCtxId, decide },
    ] = await Promise.all([
      import('@/server/referrals/fraud/registry.js'),
      import('@/server/referrals/settings.js'),
      import('@/server/referrals/service.js'),
      import('@/server/domain/referral/apply-effects.js'),
      import('@/server/domain/referral/machine.js'),
    ]);

    const settings = await getReferralSettings(db);
    const totalAgorot = amountPaidAgorot(purchase);
    const platformNetAgorot =
      (purchase as { platformFeeAmountAgorot?: number | null }).platformFeeAmountAgorot ?? 0;
    const { action } = await runFraudPipeline({
      point: 'EARN',
      ctx: {
        db,
        referralId,
        referrerUserId,
        refereeUserId,
        purchaseId: orderLineId,
        platformNetAgorot,
        visitorIdReferee: refRow.visitorIdReferee ?? undefined,
        visitorIdReferrer: refRow.visitorIdReferrer ?? undefined,
        cardFingerprintReferee: refereeCard[0]?.cardFingerprint ?? undefined,
        cardFingerprintReferrer: referrerCard[0]?.cardFingerprint ?? undefined,
      },
      userId: refereeUserId,
      referralId,
      fraudConfig: settings.fraudConfig,
      db,
      persistEvents: true,
    });

    const referralCtx = {
      ctxId: asReferralCtxId(`referral:${payload.purchaseId}:${event.id}`),
      status: refRow.status,
      quarantined: false,
    };
    const decision =
      action === 'block'
        ? decide(referralCtx, {
            kind: 'fraud_blocked',
            referralId,
            referrerUserId,
            purchaseId: orderLineId,
            sourceType: 'order_line',
            at: new Date(),
          })
        : action === 'hold'
          ? decide(referralCtx, { kind: 'fraud_held', referralId, at: new Date() })
          : decide(referralCtx, {
              kind: 'earn_qualified',
              referralId,
              refereeUserId,
              purchaseId: orderLineId,
              amountPaidAgorot: totalAgorot,
              orderLineId,
            });
    if (!decision.ok) {
      throw new Error(`[referral] ${decision.ctxId}: ${decision.message}`);
    }

    const applyCtx = {
      db,
      env: ctx as unknown as MultidealEnv,
      cfg,
      deps: { qualifyRefereeFirstPaid, accrueAffiliateCommission },
    };
    const result = await applyEffects(applyCtx, decision.effects);
    if (!result.qualified) {
      return;
    }

    const notifyDecision = decide(
      { ...decision.nextState, ctxId: referralCtx.ctxId },
      {
        kind: 'qualified_notification_requested',
        referralId,
        referrerUserId: result.qualified.referrerUserId,
        refereeUserId,
        purchaseId: orderLineId,
        rewardAgorot: result.qualified.rewardAgorot,
      },
    );
    if (!notifyDecision.ok) {
      throw new Error(`[referral] ${notifyDecision.ctxId}: ${notifyDecision.message}`);
    }
    await applyEffects(applyCtx, notifyDecision.effects);
  },
};
