import { inArray } from 'drizzle-orm'
import type { Querier } from '@platform-modules/db'
import { order, orderLine, orderStep, vendorSplit, type OrdersSchema } from './schema.js'
import type {
  BuyerRef,
  Order,
  OrderLine,
  StepRecord,
  VendorSplit,
} from './types.js'

type OrderRow = typeof order.$inferSelect
type OrderLineRow = typeof orderLine.$inferSelect
type VendorSplitRow = typeof vendorSplit.$inferSelect
type OrderStepRow = typeof orderStep.$inferSelect

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

function mapLineRow(line: OrderLineRow): OrderLine {
  return {
    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,
  }
}

function mapSplitRow(split: VendorSplitRow): VendorSplit {
  return {
    id: split.id,
    orderId: split.orderId,
    vendorId: split.vendorId ?? null,
    amount: split.amount,
    funder: split.funder,
  }
}

export function stepsFromRows(rows: OrderStepRow[]): Record<string, StepRecord> {
  const steps: Record<string, StepRecord> = {}
  for (const row of rows) {
    steps[row.stepId] = row.result as StepRecord
  }
  return steps
}

export function assembleOrder(
  row: OrderRow,
  lines: OrderLine[],
  splits: VendorSplit[],
  steps: Record<string, StepRecord>,
): Order {
  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 loadOrderChildren(
  q: Querier<OrdersSchema>,
  orderId: string,
): Promise<{
  lines: OrderLine[]
  splits: VendorSplit[]
  steps: Record<string, StepRecord>
}> {
  const [lineRows, splitRows, stepRows] = await Promise.all([
    q.select().from(orderLine).where(inArray(orderLine.orderId, [orderId])),
    q.select().from(vendorSplit).where(inArray(vendorSplit.orderId, [orderId])),
    q.select().from(orderStep).where(inArray(orderStep.orderId, [orderId])),
  ])

  return {
    lines: lineRows.map(mapLineRow),
    splits: splitRows.map(mapSplitRow),
    steps: stepsFromRows(stepRows),
  }
}

export async function loadOrdersChildrenBatch(
  q: Querier<OrdersSchema>,
  orderIds: string[],
): Promise<{
  linesByOrderId: Map<string, OrderLine[]>
  splitsByOrderId: Map<string, VendorSplit[]>
  stepsByOrderId: Map<string, Record<string, StepRecord>>
}> {
  if (orderIds.length === 0) {
    return {
      linesByOrderId: new Map(),
      splitsByOrderId: new Map(),
      stepsByOrderId: new Map(),
    }
  }

  const [lineRows, splitRows, stepRows] = await Promise.all([
    q.select().from(orderLine).where(inArray(orderLine.orderId, orderIds)),
    q.select().from(vendorSplit).where(inArray(vendorSplit.orderId, orderIds)),
    q.select().from(orderStep).where(inArray(orderStep.orderId, orderIds)),
  ])

  const linesByOrderId = new Map<string, OrderLine[]>()
  for (const line of lineRows) {
    const mapped = mapLineRow(line)
    const bucket = linesByOrderId.get(line.orderId) ?? []
    bucket.push(mapped)
    linesByOrderId.set(line.orderId, bucket)
  }

  const splitsByOrderId = new Map<string, VendorSplit[]>()
  for (const split of splitRows) {
    const mapped = mapSplitRow(split)
    const bucket = splitsByOrderId.get(split.orderId) ?? []
    bucket.push(mapped)
    splitsByOrderId.set(split.orderId, bucket)
  }

  const stepsByOrderId = new Map<string, Record<string, StepRecord>>()
  for (const step of stepRows) {
    const bucket = stepsByOrderId.get(step.orderId) ?? {}
    bucket[step.stepId] = step.result as StepRecord
    stepsByOrderId.set(step.orderId, bucket)
  }

  return { linesByOrderId, splitsByOrderId, stepsByOrderId }
}
