import { useCallback, useContext, useRef, useState } from 'react'
import { PaymentUiContext, type ConfirmPaymentOptions, type PaymentConfirmResult } from './context'
import { PaymentConfirmError, PaymentProviderError } from './errors'

type Status = 'idle' | 'confirming' | 'succeeded' | 'error'

export function useConfirmPayment(): {
  confirm: (opts: ConfirmPaymentOptions) => Promise<PaymentConfirmResult>
  /**
   * Client-side success ONLY — NOT settlement. Fulfillment requires the server webhook;
   * navigate to server-truth order status, never render fulfillment off `succeeded` alone.
   */
  status: 'idle' | 'confirming' | 'succeeded' | 'error'
  error: PaymentConfirmError | null
} {
  const ctx = useContext(PaymentUiContext)
  if (!ctx) {
    throw new PaymentProviderError('useConfirmPayment must be used inside a payment provider (e.g. StripePaymentProvider).')
  }
  const [status, setStatus] = useState<Status>('idle')
  const [error, setError] = useState<PaymentConfirmError | null>(null)
  const inFlight = useRef<Promise<PaymentConfirmResult> | null>(null)
  // Keep the latest context confirm in a ref so `confirm` identity stays stable (memoized-child safety).
  const ctxConfirm = useRef(ctx.confirm)
  ctxConfirm.current = ctx.confirm

  const confirm = useCallback((opts: ConfirmPaymentOptions): Promise<PaymentConfirmResult> => {
    // Double-submit guard (UX): ignore while a confirm is in flight. The PSP PaymentIntent
    // lifecycle is the real double-charge defense; this just avoids a redundant round-trip.
    if (inFlight.current) return inFlight.current
    setStatus('confirming')
    setError(null)
    const p = (async (): Promise<PaymentConfirmResult> => {
      const res = await ctxConfirm.current(opts)
      if (res.kind === 'succeeded') setStatus('succeeded')
      else if (res.kind === 'error') {
        setStatus('error')
        setError(res.error)
      }
      // requires_redirect: leave status 'confirming' — the browser is navigating away.
      return res
    })()
    inFlight.current = p
    void p.finally(() => {
      inFlight.current = null
    })
    return p
  }, [])

  return { confirm, status, error }
}