import { and, eq } from 'drizzle-orm'
import type { Transaction } from '@platform-modules/db'
import { OrderNotChargeableError } from './errors.js'
import { order, orderLine, vendorSplit, type OrdersSchema } from './schema.js'
import { assertUuid, type BuyerRef, type Order, type OrderLine, type VendorSplit } from './types.js'

function rowToBuyerRef(row: {
  buyerUserId: string | null
  buyerGuestEmail: string | null
}): BuyerRef {
  if (row.buyerUserId !== null) {
    return { userId: row.buyerUserId }
  }
  return { guestEmail: row.buyerGuestEmail! }
}

async function loadOrder(tx: Transaction<OrdersSchema>, orderId: string): Promise<Order> {
  const [row] = await tx.select().from(order).where(eq(order.id, orderId))
  if (!row) {
    throw new OrderNotChargeableError(orderId)
  }

  const lineRows = await tx.select().from(orderLine).where(eq(orderLine.orderId, orderId))
  const splitRows = await tx.select().from(vendorSplit).where(eq(vendorSplit.orderId, orderId))

  const lines: OrderLine[] = lineRows.map((line) => ({
    id: line.id,
    orderId: line.orderId,
    variantId: line.variantId,
    kind: line.kind,
    qty: line.qty,
    unitPrice: line.unitPrice,
    lineTotal: line.lineTotal,
    vendorId: line.vendorId ?? null,
  }))

  const splits: VendorSplit[] = splitRows.map((split) => ({
    id: split.id,
    orderId: split.orderId,
    vendorId: split.vendorId ?? null,
    amount: split.amount,
    funder: split.funder,
  }))

  return {
    id: row.id,
    buyerRef: rowToBuyerRef(row),
    status: row.status,
    currency: row.currency,
    priceMode: row.priceMode,
    subtotal: row.subtotal,
    tax: row.tax,
    discount: row.discount,
    total: row.total,
    lines,
    splits,
    fulfillmentState: { steps: {} },
  }
}

export async function claimForCharge(
  tx: Transaction<OrdersSchema>,
  orderId: string,
): Promise<Order> {
  assertUuid(orderId, 'orderId')

  const now = new Date()
  const [updated] = await tx
    .update(order)
    .set({ status: 'charging', updatedAt: now })
    .where(and(eq(order.id, orderId), eq(order.status, 'pending')))
    .returning()

  if (!updated) {
    throw new OrderNotChargeableError(orderId)
  }

  return loadOrder(tx, orderId)
}
