/**
 * Marketplace billing — records per-request billing events and computes
 * operator earnings, buyer spending, and platform revenue.
 *
 * Fee split: 20% platform, 80% operator (business decision, issue #290).
 */

import { db } from "@/lib/db";
import { logger } from "@/lib/logger";

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

/** Platform takes 20% of every marketplace transaction. */
export const PLATFORM_FEE_RATE = 0.2;

/** Minimum payout threshold in cents ($1.00). */
export const MIN_PAYOUT_CENTS = 100;

// ---------------------------------------------------------------------------
// Fee calculation (pure, no I/O)
// ---------------------------------------------------------------------------

export interface FeeBreakdown {
  priceCents: number;
  platformFeeCents: number;
  operatorShareCents: number;
}

/**
 * Calculate the 80/20 split for a given price in cents.
 * Platform fee is rounded up (ceil) so the platform never under-collects.
 * Operator share is the remainder.
 */
export function calculateFees(priceCents: number): FeeBreakdown {
  if (priceCents < 0) {
    throw new Error("priceCents must be non-negative");
  }
  const platformFeeCents = Math.ceil(priceCents * PLATFORM_FEE_RATE);
  const operatorShareCents = priceCents - platformFeeCents;
  return { priceCents, platformFeeCents, operatorShareCents };
}

// ---------------------------------------------------------------------------
// Record billing event
// ---------------------------------------------------------------------------

export interface RecordBillingParams {
  deviceId: string;
  operatorId: string;
  buyerId: string;
  model: string;
  promptTokens: number;
  completionTokens: number;
  priceCents: number;
}

/**
 * Record a marketplace billing event. Calculates fee split and persists.
 * Returns the created billing event.
 */
export async function recordBillingEvent(params: RecordBillingParams) {
  const { deviceId, operatorId, buyerId, model, promptTokens, completionTokens, priceCents } = params;
  const totalTokens = promptTokens + completionTokens;
  const { platformFeeCents, operatorShareCents } = calculateFees(priceCents);

  const event = await db.marketplaceBillingEvent.create({
    data: {
      deviceId,
      operatorId,
      buyerId,
      model,
      promptTokens,
      completionTokens,
      totalTokens,
      priceCents,
      platformFeeCents,
      operatorShareCents,
    },
  });

  logger.info(
    {
      action: "marketplace_billing_event_created",
      eventId: event.id,
      operatorId,
      buyerId,
      priceCents,
      operatorShareCents,
      platformFeeCents,
    },
    "Recorded marketplace billing event",
  );

  return event;
}

// ---------------------------------------------------------------------------
// Aggregation queries
// ---------------------------------------------------------------------------

export interface EarningsSummary {
  totalRevenue: number;
  operatorShare: number;
  platformFee: number;
  totalRequests: number;
  periodDays: number;
  dailyAverage: number;
  /** ISO timestamp of the very first booking ever received, or null if none yet. */
  firstBookingAt: string | null;
  /** Total all-time requests served (not period-limited). */
  lifetimeRequests: number;
}

/**
 * Get operator earnings for the given period (in days, default 30).
 * Aggregates from MarketplaceBillingEvent records.
 * Also returns first-booking timestamp and lifetime request count for cold-start UX.
 */
export async function getOperatorEarnings(
  operatorId: string,
  days: number = 30,
): Promise<EarningsSummary> {
  const since = new Date();
  since.setDate(since.getDate() - days);

  const [events, firstEvent, lifetimeCount] = await Promise.all([
    db.marketplaceBillingEvent.findMany({
      where: {
        operatorId,
        createdAt: { gte: since },
      },
      select: {
        priceCents: true,
        operatorShareCents: true,
        platformFeeCents: true,
      },
    }),
    db.marketplaceBillingEvent.findFirst({
      where: { operatorId },
      orderBy: { createdAt: "asc" },
      select: { createdAt: true },
    }),
    db.marketplaceBillingEvent.count({ where: { operatorId } }),
  ]);

  const totalRevenueCents = events.reduce((sum, e) => sum + e.priceCents, 0);
  const operatorShareCents = events.reduce((sum, e) => sum + e.operatorShareCents, 0);
  const platformFeeCents = events.reduce((sum, e) => sum + e.platformFeeCents, 0);

  // Convert cents to dollars for the response
  const totalRevenue = totalRevenueCents / 100;
  const operatorShare = operatorShareCents / 100;
  const platformFee = platformFeeCents / 100;
  const dailyAverage = days > 0 ? operatorShare / days : 0;

  return {
    totalRevenue,
    operatorShare,
    platformFee,
    totalRequests: events.length,
    periodDays: days,
    dailyAverage,
    firstBookingAt: firstEvent?.createdAt.toISOString() ?? null,
    lifetimeRequests: lifetimeCount,
  };
}

export interface SpendingSummary {
  totalSpent: number;
  totalRequests: number;
  periodDays: number;
  dailyAverage: number;
}

/**
 * Get buyer spending for the given period (in days, default 30).
 */
export async function getBuyerSpending(
  buyerId: string,
  days: number = 30,
): Promise<SpendingSummary> {
  const since = new Date();
  since.setDate(since.getDate() - days);

  const events = await db.marketplaceBillingEvent.findMany({
    where: {
      buyerId,
      createdAt: { gte: since },
    },
    select: {
      priceCents: true,
    },
  });

  const totalSpentCents = events.reduce((sum, e) => sum + e.priceCents, 0);
  const totalSpent = totalSpentCents / 100;
  const dailyAverage = days > 0 ? totalSpent / days : 0;

  return {
    totalSpent,
    totalRequests: events.length,
    periodDays: days,
    dailyAverage,
  };
}

// ---------------------------------------------------------------------------
// Payout helpers
// ---------------------------------------------------------------------------

export interface PendingPayout {
  pendingCents: number;
  eventCount: number;
  eventIds: string[];
}

/**
 * Get the total unpaid operator balance from billing events.
 * Returns amount in cents and the event IDs that compose it.
 */
export async function getPendingPayout(operatorId: string): Promise<PendingPayout> {
  const events = await db.marketplaceBillingEvent.findMany({
    where: {
      operatorId,
      paidOut: false,
    },
    select: {
      id: true,
      operatorShareCents: true,
    },
  });

  const pendingCents = events.reduce((sum, e) => sum + e.operatorShareCents, 0);

  return {
    pendingCents,
    eventCount: events.length,
    eventIds: events.map((e) => e.id),
  };
}

/**
 * Mark billing events as paid out with the Stripe Transfer ID.
 */
export async function markEventsPaidOut(
  eventIds: string[],
  transferId: string,
): Promise<number> {
  if (eventIds.length === 0) return 0;

  const result = await db.marketplaceBillingEvent.updateMany({
    where: { id: { in: eventIds } },
    data: {
      paidOut: true,
      payoutTransferId: transferId,
    },
  });

  logger.info(
    {
      action: "marketplace_events_paid_out",
      transferId,
      eventCount: result.count,
    },
    "Marked billing events as paid out",
  );

  return result.count;
}
