import { a as assertUuid, l as FulfillmentValidationError, t as SHIPMENT_STATUSES, u as LabelPurchaseError } from "./types-Ao3zhbMU.js";
import { sql } from "drizzle-orm";
//#region src/physical/shipment.ts
function firstRow(res) {
	return ((Array.isArray(res) ? res : res.rows) ?? [])[0];
}
function parseJsonField(value) {
	if (typeof value === "string") return JSON.parse(value);
	return value;
}
function rowToShipment(row) {
	const createdAt = row.created_at;
	const updatedAt = row.updated_at;
	return {
		id: String(row.id),
		orderId: String(row.order_id),
		lineIds: parseJsonField(row.line_ids),
		address: parseJsonField(row.address),
		status: String(row.status),
		carrierKind: row.carrier_kind == null ? null : String(row.carrier_kind),
		labelId: row.label_id == null ? null : String(row.label_id),
		trackingNumber: row.tracking_number == null ? null : String(row.tracking_number),
		createdAt: createdAt instanceof Date ? createdAt : new Date(String(createdAt)),
		updatedAt: updatedAt instanceof Date ? updatedAt : new Date(String(updatedAt))
	};
}
async function createShipment(tx, input) {
	assertUuid(input.orderId, "orderId");
	const id = crypto.randomUUID();
	const lineIdsJson = JSON.stringify(input.lineIds);
	const addressJson = JSON.stringify(input.address);
	const inserted = firstRow(await tx.execute(sql`
    INSERT INTO shipment (id, order_id, line_ids, address, status)
    VALUES (
      ${id}::uuid,
      ${input.orderId}::uuid,
      ${lineIdsJson}::jsonb,
      ${addressJson}::jsonb,
      'pending'
    )
    RETURNING
      id,
      order_id,
      line_ids,
      address,
      status,
      carrier_kind,
      label_id,
      tracking_number,
      created_at,
      updated_at
  `));
	if (!inserted) throw new Error("createShipment: insert returned no row");
	return rowToShipment(inserted);
}
//#endregion
//#region src/physical/label.ts
function defaultIdempotencyKey(shipmentId) {
	return `label:${shipmentId}`;
}
async function loadShipment(db, shipmentId) {
	const row = firstRow(await db.execute(sql`
    SELECT
      id,
      order_id,
      line_ids,
      address,
      status,
      carrier_kind,
      label_id,
      tracking_number,
      created_at,
      updated_at
    FROM shipment
    WHERE id = ${shipmentId}::uuid
  `));
	if (!row) throw new LabelPurchaseError(shipmentId, "shipment not found");
	return rowToShipment(row);
}
async function buyShippingLabel(db, shipmentId, carrier, idempotencyKey) {
	assertUuid(shipmentId, "shipmentId");
	const key = idempotencyKey ?? defaultIdempotencyKey(shipmentId);
	const shipment = await loadShipment(db, shipmentId);
	if (shipment.labelId && shipment.trackingNumber) return {
		id: shipment.labelId,
		trackingNumber: shipment.trackingNumber
	};
	let label;
	try {
		label = await carrier.buyLabel({
			shipmentId,
			address: shipment.address,
			idempotencyKey: key
		});
	} catch (e) {
		const detail = e instanceof Error ? e.message : String(e);
		throw new LabelPurchaseError(shipmentId, detail);
	}
	await db.transaction(async (tx) => {
		await tx.execute(sql`
      UPDATE shipment
      SET
        label_id = ${label.id},
        tracking_number = ${label.trackingNumber},
        status = 'labeled',
        carrier_kind = ${carrier.kind},
        updated_at = NOW()
      WHERE id = ${shipmentId}::uuid
        AND status = 'pending'
    `);
	});
	return label;
}
//#endregion
//#region src/physical/webhook.ts
async function handleCarrierWebhook(ports, carrier, req) {
	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");
	if (!firstRow(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
  `))) return;
	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
      `);
	});
}
//#endregion
//#region src/physical/track.ts
async function trackShipment(db, shipmentId, carrier) {
	assertUuid(shipmentId, "shipmentId");
	const row = firstRow(await db.execute(sql`
    SELECT
      id,
      order_id,
      line_ids,
      address,
      status,
      carrier_kind,
      label_id,
      tracking_number,
      created_at,
      updated_at
    FROM shipment
    WHERE id = ${shipmentId}::uuid
  `));
	if (!row) throw new FulfillmentValidationError("shipment");
	const shipment = rowToShipment(row);
	if (!shipment.trackingNumber) throw new FulfillmentValidationError("trackingNumber");
	const trackResult = await carrier.track(shipment.trackingNumber);
	await db.transaction(async (tx) => {
		await tx.execute(sql`
      UPDATE shipment
      SET status = ${trackResult.status}, updated_at = NOW()
      WHERE id = ${shipmentId}::uuid
    `);
	});
	return trackResult;
}
//#endregion
export { createShipment as i, handleCarrierWebhook as n, buyShippingLabel as r, trackShipment as t };
