import { createContext } from 'react'
import type { CheckoutClient, CheckoutStartInput } from './client.js'
import type { CheckoutWireError, PaymentConfirmError } from './errors.js'
import type { StaleSplit } from '@platform-modules/commerce-checkout'

export type CheckoutPhase =
  | 'idle'
  | 'starting'
  | 'awaiting_payment'
  | 'confirming'
  | 'settling'
  | 'settle_timeout'
  | 'cart_changed'
  | 'paid'
  | 'failed'
  | 'stale'

export type CheckoutState = {
  phase: CheckoutPhase
  orderId: string | null
  clientSecret: string | null
  mintCartVersion: string | number | null
  stale: StaleSplit | null
  error: CheckoutWireError | PaymentConfirmError | null
}

export const initialState: CheckoutState = {
  phase: 'idle',
  orderId: null,
  clientSecret: null,
  mintCartVersion: null,
  stale: null,
  error: null,
}

export type CheckoutAction =
  | { type: 'SUBMIT_START'; cartVersion?: string | number }
  | { type: 'AWAIT_PAYMENT'; orderId: string; clientSecret: string }
  | { type: 'SETTLING'; orderId: string }
  | { type: 'STALE'; stale: StaleSplit }
  | { type: 'CART_CHANGED' }
  | { type: 'CONFIRMING' }
  | { type: 'PAID' }
  | { type: 'FAILED'; error: CheckoutWireError | PaymentConfirmError | null }
  | { type: 'SETTLE_TIMEOUT' }
  | { type: 'RESET' }

const SETTLING_PREDECESSORS: ReadonlySet<CheckoutPhase> = new Set([
  'starting',
  'confirming',
  'idle',
  'settle_timeout',
])

/** Active attempt phases only — FAILED must not overwrite cart_changed / stale / paid / etc. */
const FAILED_PREDECESSORS: ReadonlySet<CheckoutPhase> = new Set([
  'starting',
  'awaiting_payment',
  'confirming',
  'settling',
])

/** AWAIT_PAYMENT only from an in-flight submit resolve (starting) or poll resurface (settling). */
export const AWAIT_PAYMENT_PREDECESSORS: ReadonlySet<CheckoutPhase> = new Set(['starting', 'settling'])

export function checkoutReducer(state: CheckoutState, action: CheckoutAction): CheckoutState {
  switch (action.type) {
    case 'SUBMIT_START':
      return {
        ...state,
        phase: 'starting',
        orderId: null,
        clientSecret: null,
        mintCartVersion: action.cartVersion ?? null,
        error: null,
        stale: null,
      }
    case 'AWAIT_PAYMENT':
      if (!AWAIT_PAYMENT_PREDECESSORS.has(state.phase)) return state
      return { ...state, phase: 'awaiting_payment', orderId: action.orderId, clientSecret: action.clientSecret }
    case 'SETTLING':
      if (!SETTLING_PREDECESSORS.has(state.phase)) return state
      return { ...state, phase: 'settling', orderId: action.orderId, clientSecret: null }
    case 'STALE':
      return { ...state, phase: 'stale', stale: action.stale, clientSecret: null, orderId: null }
    case 'CART_CHANGED':
      return { ...initialState, phase: 'cart_changed' }
    case 'CONFIRMING':
      return { ...state, phase: 'confirming', error: null }
    case 'PAID':
      return { ...state, phase: 'paid', clientSecret: null }
    case 'FAILED':
      if (!FAILED_PREDECESSORS.has(state.phase)) return state
      return { ...state, phase: 'failed', error: action.error }
    case 'SETTLE_TIMEOUT':
      return { ...state, phase: 'settle_timeout' }
    case 'RESET':
      return initialState
    default:
      return state
  }
}

/** What the two hooks read off context. submit/reset live in useCheckout; the provider owns the poll. */
export type CheckoutContextValue = {
  state: CheckoutState
  client: CheckoutClient
  cartId: string
  makeIdempotencyKey?: () => string
  dispatch: React.Dispatch<CheckoutAction>
  /** Imperative submit owned by the provider's stable callback (idempotency + start + branch). */
  submit: (input: Omit<CheckoutStartInput, 'idempotencyKey'>) => Promise<void>
  reset: () => void
  /** Reset-free re-poll from settle_timeout when a persisted orderId exists (§6). */
  retry: () => void
}

export const CheckoutContext = createContext<CheckoutContextValue | null>(null)