/** Invite link codes: letters + numbers, 4–32 chars */
const INVITE_CODE_RE = /^[a-zA-Z0-9]{4,32}$/

export function isValidInviteCode(code: string): boolean {
  return INVITE_CODE_RE.test(code.trim())
}

export const INVITE_STORAGE_KEY = 'invite_code'
export const INVITE_PORTAL_NAVIGATE_EVENT = 'invite-portal-navigate'

export type InvitePhase =
  | 'loading'
  | 'invalid'
  | 'captcha'
  | 'offline'
  | 'schedule'
  | 'datetime'
  | 'details'
  | 'confirmation'

export function buildInvitePath(code: string): string {
  return `/invite/${encodeURIComponent(code.trim())}`
}

export function isInvitePath(pathname: string): boolean {
  const normalized = pathname.replace(/\/$/, '') || pathname
  return /^\/invite\/[a-zA-Z0-9]{4,32}$/.test(normalized)
}

export function getInviteCodeFromPath(pathname: string): string | null {
  const match = pathname.match(/^\/invite\/([a-zA-Z0-9]{4,32})\/?$/)
  return match ? match[1] : null
}

export function storeInviteCode(code: string): void {
  if (typeof window === 'undefined') return
  try {
    sessionStorage.setItem(INVITE_STORAGE_KEY, code.trim())
  } catch {
    /* ignore */
  }
}

export function readInviteCode(): string | null {
  if (typeof window === 'undefined') return null
  try {
    return sessionStorage.getItem(INVITE_STORAGE_KEY)
  } catch {
    return null
  }
}

export function getCaptchaEntryPath(): string {
  const code = readInviteCode()
  if (code && isValidInviteCode(code)) return buildInvitePath(code)
  return '/'
}

export function redirectPathToInvitePhase(path: string): InvitePhase {
  const pathname = (path.split('?')[0] || path).trim()
  if (pathname.includes('/select-date-time')) return 'datetime'
  if (pathname.includes('/enter-details')) return 'details'
  if (pathname.includes('/confirmation')) return 'confirmation'
  if (pathname.includes('/offline')) return 'offline'
  if (pathname === '/' || pathname === '') return 'captcha'
  return 'schedule'
}

export function dispatchInvitePortalNavigate(path: string): void {
  if (typeof window === 'undefined') return
  window.dispatchEvent(
    new CustomEvent(INVITE_PORTAL_NAVIGATE_EVENT, {
      detail: { path, phase: redirectPathToInvitePhase(path) },
    })
  )
}

export function subscribeInvitePortalNavigate(
  handler: (detail: { path: string; phase: InvitePhase }) => void
): () => void {
  if (typeof window === 'undefined') return () => {}
  const listener = (event: Event) => {
    const detail = (event as CustomEvent<{ path: string; phase: InvitePhase }>).detail
    if (detail?.phase) handler(detail)
  }
  window.addEventListener(INVITE_PORTAL_NAVIGATE_EVENT, listener)
  return () => window.removeEventListener(INVITE_PORTAL_NAVIGATE_EVENT, listener)
}

/** Stay on invite URL when navigating between booking steps. */
export function clientNavigate(
  path: string,
  router: { push: (url: string) => void; replace: (url: string) => void },
  method: 'push' | 'replace' = 'push'
): void {
  if (typeof window !== 'undefined' && isInvitePath(window.location.pathname)) {
    dispatchInvitePortalNavigate(path)
    return
  }
  if (method === 'replace') router.replace(path)
  else router.push(path)
}

function getInviteStayPath(): string | null {
  if (typeof window === 'undefined') return null
  if (isInvitePath(window.location.pathname)) {
    return window.location.pathname
  }
  return null
}

export function hasInviteStayPath(): boolean {
  return getInviteStayPath() != null
}

/** True when visitor arrived via a valid invite link (stored or current URL). */
export function hasInviteAccess(): boolean {
  if (typeof window === 'undefined') return false
  if (isInvitePath(window.location.pathname)) return true
  const code = readInviteCode()
  return code != null && isValidInviteCode(code)
}
