import { eq, sql } from 'drizzle-orm'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { claimForCharge } from '../claim-for-charge.js'
import { createOrder } from '../create-order.js'
import { isRefundExceedsPaidError } from '../errors.js'
import { markPaid } from '../mark-paid.js'
import { startPg } from '../pg-harness.js'
import { refundIntent, type OrdersSchema } from '../schema.js'
import type { TransactionalDatabase } from '@platform-modules/db'
import type { NewOrder } from '../types.js'
import { claimRefundIntentInTx, settleRefundIntentInTx } from './execute-refund.js'
import { order } from '../schema.js'

const VARIANT_A = '11111111-1111-4111-8111-111111111111'
const VARIANT_B = '22222222-2222-4222-8222-222222222222'
const BUYER_ID = '33333333-3333-4333-8333-333333333333'
const CHARGE_REF = 'ch_concurrency'

function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms))
}

function orderTotal100(): NewOrder {
  return {
    idempotencyKey: crypto.randomUUID(),
    buyerRef: { userId: BUYER_ID },
    currency: 'USD',
    priceMode: 'exclusive',
    subtotal: 100n,
    tax: 0n,
    discount: 0n,
    total: 100n,
    lines: [
      {
        variantId: VARIANT_A,
        kind: 'physical',
        qty: 1,
        unitPrice: 100n,
        lineTotal: 100n,
        currency: 'USD',
        vendorId: null,
      },
    ],
    splits: [{ vendorId: null, amount: 100n, funder: 'platform' }],
  }
}

function orderTwoLines50(): NewOrder {
  return {
    idempotencyKey: crypto.randomUUID(),
    buyerRef: { userId: BUYER_ID },
    currency: 'USD',
    priceMode: 'exclusive',
    subtotal: 100n,
    tax: 0n,
    discount: 0n,
    total: 100n,
    lines: [
      {
        variantId: VARIANT_A,
        kind: 'physical',
        qty: 1,
        unitPrice: 50n,
        lineTotal: 50n,
        currency: 'USD',
        vendorId: null,
      },
      {
        variantId: VARIANT_B,
        kind: 'physical',
        qty: 1,
        unitPrice: 50n,
        lineTotal: 50n,
        currency: 'USD',
        vendorId: null,
      },
    ],
    splits: [{ vendorId: null, amount: 100n, funder: 'platform' }],
  }
}

async function sumPendingExecuted(
  db: TransactionalDatabase<OrdersSchema>,
  orderId: string,
): Promise<bigint> {
  const res = await db.execute(sql`
    SELECT COALESCE(SUM(amount), 0) AS total
    FROM refund_intent
    WHERE order_id = ${orderId}::uuid
      AND status IN ('pending', 'executed')
  `)
  const rows = (Array.isArray(res) ? res : (res as { rows?: { total: unknown }[] }).rows) ?? []
  return BigInt(String(rows[0]?.total ?? 0))
}

describe('executeRefund concurrency (real Postgres)', () => {
  let db: TransactionalDatabase<OrdersSchema>
  let stop: (() => Promise<void>) | undefined

  beforeAll(async () => {
    const pg = await startPg()
    db = pg.db
    stop = pg.stop
  }, 120_000)

  afterAll(async () => {
    await stop?.()
  }, 30_000)

  it('locked-intent cap: concurrent partial refunds — one wins, one RefundExceedsPaidError', async () => {
    const created = await db.transaction((tx) => createOrder(tx, orderTotal100()))
    const orderId = created.id
    await db.transaction((tx) => claimForCharge(tx, orderId))
    await db.transaction((tx) => markPaid(tx, orderId, CHARGE_REF))

    let resolveHold!: () => void
    const hold = new Promise<void>((resolve) => {
      resolveHold = resolve
    })

    const txA = db.transaction(async (txA) => {
      await claimRefundIntentInTx(txA, orderId, 60n)
      await hold
    })

    const txB = db.transaction(async (txB) => {
      await claimRefundIntentInTx(txB, orderId, 60n)
    })

    await sleep(400)
    resolveHold()

    const results = await Promise.allSettled([txA, txB])
    const fulfilled = results.filter((r) => r.status === 'fulfilled')
    const rejected = results.filter((r) => r.status === 'rejected')

    expect(fulfilled).toHaveLength(1)
    expect(rejected).toHaveLength(1)
    expect(isRefundExceedsPaidError((rejected[0] as PromiseRejectedResult).reason)).toBe(true)

    const intents = await db.select().from(refundIntent).where(eq(refundIntent.orderId, orderId))
    expect(intents).toHaveLength(1)

    const capped = await sumPendingExecuted(db, orderId)
    expect(capped).toBeLessThanOrEqual(100n)
  })

  it('order-status recompute: concurrent settles of both halves → order refunded, not stuck partial', async () => {
    // Two pending intents (50 + 50) on a fully-paid 100 order. Settling both
    // concurrently must converge to 'refunded'. Without FOR UPDATE on the order
    // row in the settle tx, each recompute reads SUM(executed) under its own
    // snapshot (neither sees the other's uncommitted intent), both write the
    // stale 'partially_refunded', and the order is wrongly stuck partial.
    const created = await db.transaction((tx) => createOrder(tx, orderTwoLines50()))
    const orderId = created.id
    await db.transaction((tx) => claimForCharge(tx, orderId))
    await db.transaction((tx) => markPaid(tx, orderId, CHARGE_REF))

    const a = await db.transaction((tx) => claimRefundIntentInTx(tx, orderId, 50n))
    const b = await db.transaction((tx) => claimRefundIntentInTx(tx, orderId, 50n))

    await Promise.all([
      db.transaction((tx) => settleRefundIntentInTx(tx, a.intentId, orderId, 're_a')),
      db.transaction((tx) => settleRefundIntentInTx(tx, b.intentId, orderId, 're_b')),
    ])

    const [row] = await db.select().from(order).where(eq(order.id, orderId))
    expect(row?.status).toBe('refunded')
  })
})
