/**
 * Referrals Service
 * Manage referral codes, conversions, and rewards
 *
 * All database calls go through direct Convex queries/mutations.
 */

import { convexQuery, convexMutation } from '@/lib/convex-client';
import { logger } from '@/lib/logger';

const log = logger.scope('ReferralsService');

// ═══════════════════════════════════════════════════════════════════════
// Types
// ═══════════════════════════════════════════════════════════════════════

interface CreateReferralCodeInput {
  name: string;
  description?: string | null;
  rewardType: string;
  rewardValue: number;
  rewardCurrency: string;
  maxUses?: number | null;
  expiresAt?: string | null;
}

interface ConvertReferralInput {
  referralId: string;
  referredCustomerName?: string;
}

interface IssueRewardInput {
  referralId: string;
}

interface ReferralFilterInput {
  status?: string;
  search?: string;
  page?: number;
  limit?: number;
  sortBy?: string;
  sortOrder?: 'asc' | 'desc';
}

interface ReferralDoc {
  _id: string;
  userId: string;
  code: string;
  name: string;
  description?: string;
  rewardType: string;
  rewardValue: number;
  rewardCurrency: string;
  maxUses?: number;
  expiresAt?: number;
  uses: number;
  conversions: number;
  rewardsIssued: number;
  status: string;
  lastConvertedAt?: number;
  lastRewardAt?: number;
  createdAt: number;
  updatedAt: number;
}

interface ReferralStats {
  totalCodes: number;
  totalUses: number;
  totalConversions: number;
  totalRewards: number;
  conversionRate: number;
  byStatus: Record<string, number>;
}

// ═══════════════════════════════════════════════════════════════════════
// Helpers
// ═══════════════════════════════════════════════════════════════════════

function generateReferralCode(): string {
  const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
  let code = 'REF-';
  for (let i = 0; i < 6; i++) {
    code += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return code;
}

// ═══════════════════════════════════════════════════════════════════════
// Referral CRUD
// ═══════════════════════════════════════════════════════════════════════

export async function createReferralCode(userId: string, input: CreateReferralCodeInput) {
  // Generate unique code with retry
  let code = generateReferralCode();
  let attempts = 0;
  while (attempts < 5) {
    const existing = await convexQuery<ReferralDoc | null>(
      'reviews/referral_queries:getReferralByCode',
      { code },
    );
    if (!existing) break;
    code = generateReferralCode();
    attempts++;
  }

  const referralId = await convexMutation<string>(
    'reviews/referral_mutations:createReferral',
    {
      userId,
      code,
      name: input.name,
      description: input.description ?? undefined,
      rewardType: input.rewardType,
      rewardValue: input.rewardValue,
      rewardCurrency: input.rewardCurrency,
      maxUses: input.maxUses ?? undefined,
      expiresAt: input.expiresAt ? new Date(input.expiresAt).getTime() : undefined,
    },
  );

  if (!referralId) {
    throw new Error('Failed to create referral code in Convex');
  }

  log.info('Referral code created', { referralId, code, userId });

  return {
    id: referralId,
    userId,
    code,
    name: input.name,
    description: input.description ?? null,
    rewardType: input.rewardType,
    rewardValue: input.rewardValue,
    rewardCurrency: input.rewardCurrency,
    maxUses: input.maxUses ?? null,
    expiresAt: input.expiresAt ?? null,
    uses: 0,
    conversions: 0,
    rewardsIssued: 0,
    status: 'active',
  };
}

export async function listReferrals(userId: string, filters: ReferralFilterInput) {
  const page = filters.page ?? 1;
  const limit = Math.min(filters.limit ?? 20, 100);

  // Convex query supports userId + optional status filter
  const allReferrals = await convexQuery<ReferralDoc[]>(
    'reviews/referral_queries:getReferrals',
    {
      userId,
      status: filters.status ?? undefined,
      limit: 500, // fetch more for search/pagination in-memory
    },
  );

  let referrals = allReferrals ?? [];

  // Client-side search filter (Convex doesn't have text search on this table)
  if (filters.search) {
    const search = filters.search.toLowerCase();
    referrals = referrals.filter(
      (r) =>
        r.name?.toLowerCase().includes(search) ||
        r.code?.toLowerCase().includes(search) ||
        r.description?.toLowerCase().includes(search),
    );
  }

  // Sort
  const sortBy = filters.sortBy ?? 'createdAt';
  const sortOrder = filters.sortOrder ?? 'desc';
  referrals.sort((a, b) => {
    const aVal = (a as Record<string, unknown>)[sortBy];
    const bVal = (b as Record<string, unknown>)[sortBy];
    if (aVal == null && bVal == null) return 0;
    if (aVal == null) return 1;
    if (bVal == null) return -1;
    const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
    return sortOrder === 'desc' ? -cmp : cmp;
  });

  const total = referrals.length;
  const skip = (page - 1) * limit;
  const paginated = referrals.slice(skip, skip + limit);

  return {
    referrals: paginated,
    total,
    page,
    limit,
    totalPages: Math.ceil(total / limit),
  };
}

export async function convertReferral(userId: string, input: ConvertReferralInput) {
  // Look up the referral by ID, ensure it belongs to the user and is active
  const existing = await convexQuery<ReferralDoc | null>(
    'reviews/referral_queries:getReferralById',
    { id: input.referralId },
  );

  if (!existing || existing.userId !== userId || existing.status !== 'active') {
    return null;
  }

  // Check max uses
  if (existing.maxUses && existing.conversions >= existing.maxUses) {
    return null;
  }

  const updatedId = await convexMutation<string>(
    'reviews/referral_mutations:updateReferral',
    {
      id: input.referralId,
      conversions_increment: 1,
      lastConvertedAt: Date.now(),
    },
  );

  if (!updatedId) return null;

  log.info('Referral converted', {
    referralId: input.referralId,
    customer: input.referredCustomerName,
    userId,
  });

  return {
    ...existing,
    conversions: existing.conversions + 1,
    lastConvertedAt: Date.now(),
  };
}

export async function issueReward(userId: string, input: IssueRewardInput) {
  const existing = await convexQuery<ReferralDoc | null>(
    'reviews/referral_queries:getReferralById',
    { id: input.referralId },
  );

  if (!existing || existing.userId !== userId) {
    return null;
  }

  const updatedId = await convexMutation<string>(
    'reviews/referral_mutations:updateReferral',
    {
      id: input.referralId,
      rewardsIssued_increment: 1,
      lastRewardAt: Date.now(),
    },
  );

  if (!updatedId) return null;

  log.info('Referral reward issued', { referralId: input.referralId, userId });

  return {
    ...existing,
    rewardsIssued: existing.rewardsIssued + 1,
    lastRewardAt: Date.now(),
  };
}

export async function getReferralStats(userId: string): Promise<ReferralStats> {
  const stats = await convexQuery<ReferralStats>(
    'reviews/referral_queries:getReferralStats',
    { userId },
  );

  return (
    stats ?? {
      totalCodes: 0,
      totalUses: 0,
      totalConversions: 0,
      totalRewards: 0,
      conversionRate: 0,
      byStatus: {},
    }
  );
}

export async function lookupByCode(code: string) {
  const referral = await convexQuery<ReferralDoc | null>(
    'reviews/referral_queries:getReferralByCode',
    { code: code.toUpperCase(), status: 'active' },
  );

  if (!referral) return null;

  // Check expiry
  if (referral.expiresAt && referral.expiresAt < Date.now()) return null;

  // Record the use
  await convexMutation('reviews/referral_mutations:updateReferral', {
    id: referral._id,
    uses_increment: 1,
  });

  return {
    id: referral._id,
    code: referral.code,
    name: referral.name,
    description: referral.description,
    rewardType: referral.rewardType,
    rewardValue: referral.rewardValue,
    rewardCurrency: referral.rewardCurrency,
    expiresAt: referral.expiresAt,
  };
}
