import { sql } from 'drizzle-orm'
import type { Transaction } from '@platform-modules/db'
import { FulfillmentValidationError } from '../errors.js'
import type { FulfillmentSchema } from '../schema.js'
import { assertPositiveInt, assertUuid, type Voucher } from '../types.js'

type SqlRow = Record<string, unknown>

function allRows(res: unknown): SqlRow[] {
  return (Array.isArray(res) ? res : (res as { rows?: SqlRow[] }).rows) ?? []
}

function rowToVoucher(row: SqlRow): Voucher {
  const expiresAt = row.expires_at
  const redeemedAt = row.redeemed_at
  const createdAt = row.created_at
  return {
    id: String(row.id),
    orderId: String(row.order_id),
    lineId: String(row.line_id),
    unitIndex: Number(row.unit_index),
    vendorId: row.vendor_id == null ? null : String(row.vendor_id),
    state: String(row.state) as Voucher['state'],
    expiresAt:
      expiresAt == null
        ? null
        : expiresAt instanceof Date
          ? expiresAt
          : new Date(String(expiresAt)),
    redeemedAt:
      redeemedAt == null
        ? null
        : redeemedAt instanceof Date
          ? redeemedAt
          : new Date(String(redeemedAt)),
    createdAt:
      createdAt instanceof Date ? createdAt : new Date(String(createdAt)),
  }
}

export async function issueVoucher(
  tx: Transaction<FulfillmentSchema>,
  input: {
    orderId: string
    lineId: string
    qty: number
    vendorId: string | null
    expiresAt: Date | null
  },
): Promise<Voucher[]> {
  assertUuid(input.orderId, 'orderId')
  assertPositiveInt(input.qty, 'qty')
  if (input.lineId.length === 0) {
    throw new FulfillmentValidationError('lineId')
  }

  const valueRows = Array.from({ length: input.qty }, (_, unitIndex) => {
    const id = crypto.randomUUID()
    return sql`(
      ${id}::uuid,
      ${input.orderId}::uuid,
      ${input.lineId},
      ${unitIndex},
      ${input.vendorId},
      'UNREDEEMED',
      ${input.expiresAt}
    )`
  })

  await tx.execute(sql`
    INSERT INTO voucher (id, order_id, line_id, unit_index, vendor_id, state, expires_at)
    VALUES ${sql.join(valueRows, sql`, `)}
    ON CONFLICT (order_id, line_id, unit_index) DO NOTHING
    RETURNING id, order_id, line_id, unit_index, vendor_id, state, expires_at, redeemed_at, created_at
  `)

  const selectRes = await tx.execute(sql`
    SELECT id, order_id, line_id, unit_index, vendor_id, state, expires_at, redeemed_at, created_at
    FROM voucher
    WHERE order_id = ${input.orderId}::uuid
      AND line_id = ${input.lineId}
    ORDER BY unit_index ASC
  `)

  const vouchers = allRows(selectRes).map(rowToVoucher)
  return vouchers.filter((v) => v.unitIndex >= 0 && v.unitIndex < input.qty)
}
