import { sql } from 'drizzle-orm'
import type { Transaction } from '@platform-modules/db'
import { PromoQuotaExhaustedError } from './errors.js'
import type { PromotionsSchema } from './schema.js'
import { assertUuid } from './types.js'

type SqlRow = Record<string, unknown>

function firstRow(res: unknown): SqlRow | undefined {
  const rows = (Array.isArray(res) ? res : (res as { rows?: SqlRow[] }).rows) ?? []
  return rows[0]
}

export async function recordRedemption(
  tx: Transaction<PromotionsSchema>,
  promoId: string,
  userId: string | null,
): Promise<void> {
  assertUuid(promoId, 'promoId')

  const capRes = await tx.execute(sql`
    SELECT per_user_cap
    FROM promo
    WHERE id = ${promoId}::uuid
  `)
  const capRow = firstRow(capRes)
  const perUserCap =
    capRow?.per_user_cap === null || capRow?.per_user_cap === undefined
      ? null
      : Number(capRow.per_user_cap)

  if (userId !== null && perUserCap !== null) {
    const perUserRes = await tx.execute(sql`
      INSERT INTO promo_user_usage (promo_id, user_id, uses)
      VALUES (${promoId}::uuid, ${userId}, 1)
      ON CONFLICT (promo_id, user_id)
      DO UPDATE SET
        uses = promo_user_usage.uses + 1,
        updated_at = NOW()
      WHERE promo_user_usage.uses < ${perUserCap}
      RETURNING uses
    `)
    if (!firstRow(perUserRes)) {
      throw new PromoQuotaExhaustedError('per_user')
    }
  }

  const globalRes = await tx.execute(sql`
    UPDATE promo
    SET uses = uses + 1, updated_at = NOW()
    WHERE id = ${promoId}::uuid
      AND (max_uses IS NULL OR uses < max_uses)
    RETURNING uses
  `)
  if (!firstRow(globalRes)) {
    throw new PromoQuotaExhaustedError('global')
  }
}
