import { sql } from 'drizzle-orm'
import { FulfillmentValidationError } from '../errors.js'
import type { CarrierAdapter, FulfillmentPorts } from '../seams.js'
import { assertUuid, SHIPMENT_STATUSES } from '../types.js'
import { firstRow } from './shipment.js'

export async function handleCarrierWebhook(
  ports: FulfillmentPorts,
  carrier: CarrierAdapter,
  req: Request,
): Promise<void> {
  const ev = await carrier.verifyWebhook(req)
  if (ev == null) {
    throw new FulfillmentValidationError('webhook')
  }

  const shipmentId = ev.shipmentId
  if (shipmentId) {
    assertUuid(shipmentId, 'shipmentId')
  }

  if (!SHIPMENT_STATUSES.includes(ev.status)) {
    throw new FulfillmentValidationError('webhookStatus')
  }

  const claimRes = await ports.db.execute(sql`
    INSERT INTO carrier_webhook_event (event_id, shipment_id)
    VALUES (${ev.eventId}, ${shipmentId ?? null}::uuid)
    ON CONFLICT (event_id) DO NOTHING
    RETURNING event_id
  `)

  if (!firstRow(claimRes)) {
    return
  }

  // M6: the claim row is intentionally NOT released if the status side effect
  // below throws — the eventId stays claimed, surfacing for investigation
  // rather than allowing a silent re-apply on redelivery.
  if (shipmentId) {
    await ports.db.transaction(async (tx) => {
      await tx.execute(sql`
        UPDATE shipment
        SET status = ${ev.status}, updated_at = NOW()
        WHERE id = ${shipmentId}::uuid
      `)
    })
  }
}
