import { fulfillOrder } from '@platform-modules/commerce-fulfillment'
import {
  getOrderById,
  markPaid,
  type Order,
  type OrdersSchema,
} from '@platform-modules/commerce-orders'
import type { Transaction } from '@platform-modules/db'
import { FulfillmentIncompleteError, OrderNotChargeableError } from './errors.js'
import type { CheckoutDeps } from './types.js'

export async function settleCheckout<S extends OrdersSchema>(
  deps: CheckoutDeps<S>,
  orderId: string,
  providerRef: string,
  providerAmount?: number,
): Promise<Order> {
  // Trust-boundary floor: a webhook-supplied fractional amount must reject with
  // the typed error, never leak a bare BigInt RangeError from the compare below.
  if (providerAmount !== undefined && !Number.isInteger(providerAmount)) {
    throw new OrderNotChargeableError(orderId)
  }

  let paidOrder: Order

  if (providerRef === '') {
    const loaded = await deps.db.transaction(async (tx) => {
      const order = await getOrderById(tx as unknown as Transaction<OrdersSchema>, orderId, {
        isAdmin: true,
      })
      if (!order) {
        throw new OrderNotChargeableError(orderId)
      }
      return order
    })

    if (loaded.status === 'paid') {
      paidOrder = loaded
    } else if (loaded.status === 'charging') {
      return loaded
    } else {
      throw new OrderNotChargeableError(orderId)
    }
  } else {
    paidOrder = await deps.db.transaction(async (tx) => {
      const ordersTx = tx as unknown as Transaction<OrdersSchema>
      if (providerAmount !== undefined) {
        const preOrder = await getOrderById(ordersTx, orderId, { isAdmin: true })
        if (!preOrder) {
          throw new OrderNotChargeableError(orderId)
        }
        if (BigInt(providerAmount) < preOrder.total) {
          throw new OrderNotChargeableError(orderId)
        }
      }
      await markPaid(ordersTx, orderId, providerRef)
      const order = await getOrderById(ordersTx, orderId, { isAdmin: true })
      if (!order) {
        throw new OrderNotChargeableError(orderId)
      }
      await deps.onSettle?.(tx, order)
      return order
    })
  }

  const result = await fulfillOrder(deps.fulfillment, paidOrder)
  if (result.overall !== 'fulfilled') {
    throw new FulfillmentIncompleteError({
      orderId,
      overall: result.overall === 'unfulfillable' ? 'unfulfillable' : 'partial',
      failures: result.lines
        .filter((line) => line.kind === 'unfulfillable')
        .map((line) => ({ lineId: line.lineId, error: line.error })),
    })
  }

  return paidOrder
}
