// apps/web/src/server/referrals/enrollment.ts
import { eq, and } from 'drizzle-orm';
import { affiliateEnrollments, users, referralLinks } from '@/server/db/schema';
import type { DrizzleClient } from '@/server/db/client';
import type { ReferralSettings } from './settings';
import { randomUUID } from 'crypto';
import {
  createAffiliateEnrollment,
  createAffiliateReferralLink,
} from '@/server/db/queries/referrals/referral-writes.js';

export type EnrollInput = {
  tosAccepted: boolean;
  primaryChannel: 'social' | 'blog' | 'youtube' | 'whatsapp' | 'other';
  channelUrl?: string;
  audienceSize?: 'lt_1k' | '1k_10k' | '10k_100k' | 'gt_100k';
  notes?: string;
};

export type EnrollResult = {
  enrollmentId: string;
  referralLink: string | null;
  status: 'active';
  kycRequired: true;
  kycCompleted: boolean;
  alreadyEnrolled: boolean;
};

/**
 * Enroll a user in the affiliate program.
 * Open enrollment — no admin queue. Admin can suspend after the fact.
 */
export async function enrollAffiliate(
  db: DrizzleClient,
  userId: string,
  input: EnrollInput,
  settings: Pick<ReferralSettings, 'tosVersion'>,
): Promise<EnrollResult> {
  if (!input.tosAccepted) {
    throw new Error('TOS_NOT_ACCEPTED');
  }

  // Check user exists and is not banned/frozen.
  const userRows = await db
    .select({ id: users.id, accountState: users.accountState })
    .from(users)
    .where(eq(users.id, userId))
    .limit(1);

  if (userRows.length === 0) throw new Error('USER_NOT_FOUND');
  const user = userRows[0];
  if (!user) throw new Error('USER_NOT_FOUND');
  if (user.accountState !== 'ACTIVE') throw new Error('USER_BANNED');

  // Check for existing enrollment.
  const existingRows = await db
    .select()
    .from(affiliateEnrollments)
    .where(eq(affiliateEnrollments.userId, userId))
    .limit(1);

  if (existingRows.length > 0) {
    const existing = existingRows[0];
    if (!existing) throw new Error('ENROLLMENT_NOT_FOUND');
    // Retrieve their referral link if it exists.
    const linkRows = await db
      .select({ code: referralLinks.code })
      .from(referralLinks)
      .where(and(eq(referralLinks.ownerUserId, userId), eq(referralLinks.kind, 'affiliate')))
      .limit(1);

    return {
      enrollmentId: existing.id,
      referralLink: linkRows[0] ? `https://multi.deal/?ref=${linkRows[0].code}` : null,
      status: 'active',
      kycRequired: true,
      kycCompleted: existing.stripePayoutsEnabled,
      alreadyEnrolled: true,
    };
  }

  // Insert new enrollment.
  const insertResult = await createAffiliateEnrollment(db, {
    userId,
    status: 'active',
    tosAcceptedAt: new Date(),
    tosVersion: settings.tosVersion,
  });

  const newEnrollment = insertResult[0];
  if (!newEnrollment) throw new Error('ENROLLMENT_INSERT_FAILED');

  // Ensure an affiliate referral_links row exists (create if absent).
  const existingLinkRows = await db
    .select({ code: referralLinks.code })
    .from(referralLinks)
    .where(and(eq(referralLinks.ownerUserId, userId), eq(referralLinks.kind, 'affiliate')))
    .limit(1);

  let refCode: string;
  if (existingLinkRows.length > 0) {
    const existingCode = existingLinkRows[0]?.code;
    if (!existingCode) throw new Error('REFERRAL_LINK_NOT_FOUND');
    refCode = existingCode;
  } else {
    // Generate a new referral link code.
    const code = randomUUID().replace(/-/g, '').slice(0, 10).toUpperCase();
    await createAffiliateReferralLink(db, {
      ownerUserId: userId,
      code,
      kind: 'affiliate',
      active: true,
    });
    refCode = code;
  }

  return {
    enrollmentId: newEnrollment.id,
    referralLink: `https://multi.deal/?ref=${refCode}`,
    status: 'active',
    kycRequired: true,
    kycCompleted: false,
    alreadyEnrolled: false,
  };
}

/**
 * Get the enrollment row for a user, or null if not enrolled.
 */
export async function getAffiliateEnrollment(db: DrizzleClient, userId: string) {
  const rows = await db
    .select()
    .from(affiliateEnrollments)
    .where(eq(affiliateEnrollments.userId, userId))
    .limit(1);
  return rows[0] ?? null;
}
