import { createHmac, timingSafeEqual } from 'node:crypto'
import { z } from 'zod'

const order = z.object({
  workspace_trip_ref: z.string().regex(/^HH-(?:[1-9]\d{8}|[A-Z0-9]{6})$/),
  synchron_order_token: z.string().regex(/^\d{8}$/),
  synchron_order_id: z.string().trim().min(1).max(100).optional(),
})

/** Order/permit events: the trip is identified by its nine-digit Workspace reference. */
const orderCallbackSchema = z.discriminatedUnion('event', [
  order.extend({ event: z.literal('order.created') }),
  order.extend({
    event: z.literal('permit.attached'),
    item_id: z.string().trim().min(1).max(100),
    state_code: z.string().regex(/^[A-Z]{2}$/),
    permit_url: z.string().url().max(2048),
    permit_number: z.string().trim().max(100).optional(),
    effective_date: z.iso.date().optional(),
    expiration_date: z.iso.date().optional(),
  }),
])

/**
 * PROVISIONAL — route purchase payment events (task §6.4, 2026-09-29).
 *
 * Synchron has not yet published its payment callback contract; these
 * shapes are HHA's proposal and are listed under "Waiting on Synchron" in
 * docs/SYNCHRON-PAYMENTS-INTEGRATION.md. They are keyed by the HHA purchase
 * reference (`hhp_…`), never by a trip or order token, and carry no card
 * data. `event_id` must be stable per delivery so retries are no-ops.
 */
const purchase = z.object({
  purchase_reference: z.string().regex(/^hhp_[A-Z2-7]{20}$/),
  event_id: z.string().trim().min(1).max(200),
  synchron_purchase_token: z.string().trim().min(1).max(200).optional(),
  stripe_checkout_session_id: z.string().trim().min(1).max(200).optional(),
  stripe_payment_intent_id: z.string().trim().min(1).max(200).optional(),
  stripe_charge_id: z.string().trim().min(1).max(200).optional(),
  paid_at: z.iso.datetime({ offset: true }).optional(),
})

const purchaseCallbackSchema = z.discriminatedUnion('event', [
  purchase.extend({ event: z.literal('purchase.payment_confirmed') }),
  purchase.extend({ event: z.literal('purchase.payment_failed') }),
  purchase.extend({ event: z.literal('purchase.refunded') }),
])

export const synchronCallbackSchema = z.union([orderCallbackSchema, purchaseCallbackSchema])

export type SynchronOrderCallback = z.infer<typeof orderCallbackSchema>
export type SynchronPurchaseCallback = z.infer<typeof purchaseCallbackSchema>
export type SynchronPurchaseEvent = SynchronPurchaseCallback['event']

export function isPurchaseCallback(input: SynchronCallback): input is SynchronPurchaseCallback {
  return input.event.startsWith('purchase.')
}

export type SynchronCallback = z.infer<typeof synchronCallbackSchema>

export function verifySynchronSignature(
  body: string,
  timestamp: string | null,
  signature: string | null,
  secret: string,
  nowSeconds = Math.floor(Date.now() / 1000),
): boolean {
  if (secret.length < 32 || !timestamp || !signature || !/^\d{10}$/.test(timestamp)) return false
  if (Math.abs(nowSeconds - Number(timestamp)) > 300) return false
  const supplied = signature.replace(/^sha256=/, '')
  if (!/^[0-9a-f]{64}$/i.test(supplied)) return false
  const expected = createHmac('sha256', secret).update(`${timestamp}.${body}`).digest()
  return timingSafeEqual(Buffer.from(supplied, 'hex'), expected)
}

/** Only provider-owned HTTPS hosts may be fetched by the callback server. */
export function isSynchronPermitUrl(value: string): boolean {
  try {
    const url = new URL(value)
    const hosts = (process.env.SYNCHRON_PERMIT_FILE_HOSTS ||
      'permits.synchrontms.com,synchrontms.s3.us-east-2.amazonaws.com')
      .split(',').map((host) => host.trim().toLowerCase()).filter(Boolean)
    return url.protocol === 'https:' && !url.username && !url.password &&
      !url.port && hosts.includes(url.hostname.toLowerCase())
  } catch {
    return false
  }
}
