import { sql } from 'drizzle-orm'
import type { Transaction } from '@platform-modules/db'
import { FulfillmentValidationError } from '../errors.js'
import type { FulfillmentSchema } from '../schema.js'
import { assertUuid, type AccessGrant } 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]
}

function rowToAccessGrant(row: SqlRow): AccessGrant {
  const createdAt = row.created_at
  return {
    id: String(row.id),
    orderId: String(row.order_id),
    itemId: String(row.item_id),
    ownerKey: String(row.owner_key),
    blobKey: String(row.blob_key),
    createdAt:
      createdAt instanceof Date ? createdAt : new Date(String(createdAt)),
  }
}

export async function grantDigitalAccess(
  tx: Transaction<FulfillmentSchema>,
  input: {
    orderId: string
    itemId: string
    ownerKey: string
    blobKey: string
  },
): Promise<AccessGrant> {
  assertUuid(input.orderId, 'orderId')
  if (input.itemId.length === 0) {
    throw new FulfillmentValidationError('itemId')
  }
  if (!/^(user|email):./.test(input.ownerKey)) {
    throw new FulfillmentValidationError('ownerKey')
  }
  if (input.blobKey.length === 0) {
    throw new FulfillmentValidationError('blobKey')
  }

  const id = crypto.randomUUID()

  const insertRes = await tx.execute(sql`
    INSERT INTO access_grant (id, order_id, item_id, owner_key, blob_key)
    VALUES (
      ${id}::uuid,
      ${input.orderId}::uuid,
      ${input.itemId},
      ${input.ownerKey},
      ${input.blobKey}
    )
    ON CONFLICT (order_id, item_id, owner_key) DO NOTHING
    RETURNING id, order_id, item_id, owner_key, blob_key, created_at
  `)

  const inserted = firstRow(insertRes)
  if (inserted) {
    return rowToAccessGrant(inserted)
  }

  const selectRes = await tx.execute(sql`
    SELECT id, order_id, item_id, owner_key, blob_key, created_at
    FROM access_grant
    WHERE order_id = ${input.orderId}::uuid
      AND item_id = ${input.itemId}
      AND owner_key = ${input.ownerKey}
  `)
  const existing = firstRow(selectRes)
  if (!existing) {
    throw new FulfillmentValidationError('grant')
  }
  return rowToAccessGrant(existing)
}
