import { useState } from 'react'
import {
  defaultConsent,
  type ConsentCategory,
  type ConsentState,
} from '@platform-modules/content/privacy'
import { useConsent, type ConsentStorage } from './useConsent.js'

const DEFAULT_CATEGORIES: ConsentCategory[] = ['analytics', 'marketing', 'preferences']

// Trust-boundary hardening: an adopter may bind `policyHref` from a CMS field. React does not strip
// dangerous URL schemes, so render only scheme-less (relative/anchor/query) or http/https/mailto hrefs;
// drop the link for anything else (javascript:/data:/vbscript:). Test on a control-char-stripped copy —
// browsers remove ASCII whitespace/control chars before parsing the scheme (so `java\tscript:` can't bypass).
function safePolicyHref(href: string | undefined): string | undefined {
  if (href == null) return undefined
  const cleaned = href.replace(/[\u0000-\u0020]+/g, '')
  const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(cleaned)
  if (!scheme) return href // no scheme → relative/anchor/query → safe
  const s = scheme[1]!.toLowerCase() // group 1 present whenever the regex matched
  return s === 'http' || s === 'https' || s === 'mailto' ? href : undefined
}

const DEFAULT_CATEGORY_LABELS: Record<ConsentCategory, string> = {
  necessary: 'Strictly necessary',
  analytics: 'Analytics',
  marketing: 'Marketing',
  preferences: 'Preferences',
}

export interface ConsentBannerLabels {
  title?: string
  description?: string
  acceptAll?: string
  rejectAll?: string
  save?: string
  policyLink?: string
  categoryLabels?: Partial<Record<ConsentCategory, string>>
}

export interface ConsentBannerProps {
  version: string
  storage?: ConsentStorage
  /** NON-necessary categories shown. Default: analytics, marketing, preferences. */
  categories?: ConsentCategory[]
  policyHref?: string
  labels?: ConsentBannerLabels
  /** Controlled override for re-consent ("Manage cookies"). show = open ?? (ready && !consent). */
  open?: boolean
  onChange?: (state: ConsentState) => void
  className?: string
}

export function ConsentBanner({
  version,
  storage,
  categories = DEFAULT_CATEGORIES,
  policyHref,
  labels,
  open,
  onChange,
  className,
}: ConsentBannerProps) {
  const { ready, consent, save, acceptAll, rejectAll } = useConsent({ version, storage })
  // privacy-by-default: granular selection starts from defaultConsent (only necessary on)
  const [selection, setSelection] = useState<Record<ConsentCategory, boolean>>(
    () => defaultConsent().categories,
  )

  const show = open ?? (ready && consent == null)
  if (!show) return null

  const title = labels?.title ?? 'Cookie preferences'
  const description =
    labels?.description ??
    'We use cookies to run this site. Choose which categories to allow. Necessary cookies are always on.'
  const catLabel = (c: ConsentCategory) => labels?.categoryLabels?.[c] ?? DEFAULT_CATEGORY_LABELS[c]
  const href = safePolicyHref(policyHref)

  // Persist FIRST, then notify. `onChange?.(acceptAll())` would short-circuit the mutator when
  // onChange is omitted (an optional call does not evaluate its argument) — consent would silently
  // not be captured. Each handler must persist independent of onChange (spec §5.3).
  const handleAcceptAll = () => {
    const state = acceptAll()
    onChange?.(state)
  }
  const handleRejectAll = () => {
    const state = rejectAll()
    onChange?.(state)
  }
  const handleSave = () => {
    const state = save(selection)
    onChange?.(state)
  }

  return (
    <section role="region" aria-label={title} className={className}>
      <h2>{title}</h2>
      <p>{description}</p>
      {href ? (
        <p>
          <a href={href}>{labels?.policyLink ?? 'Privacy policy'}</a>
        </p>
      ) : null}
      <fieldset>
        <legend>Cookie categories</legend>
        <label>
          <input type="checkbox" checked disabled aria-label={catLabel('necessary')} readOnly />
          {catLabel('necessary')}
        </label>
        {categories.map((c) => (
          <label key={c}>
            <input
              type="checkbox"
              aria-label={catLabel(c)}
              checked={selection[c] ?? false}
              onChange={(e) => setSelection((s) => ({ ...s, [c]: e.target.checked }))}
            />
            {catLabel(c)}
          </label>
        ))}
      </fieldset>
      <div>
        {/* Reject all is a sibling control of equal prominence to Accept all (a11y: reject as easy as accept). */}
        <button type="button" onClick={handleAcceptAll}>
          {labels?.acceptAll ?? 'Accept all'}
        </button>
        <button type="button" onClick={handleRejectAll}>
          {labels?.rejectAll ?? 'Reject all'}
        </button>
        <button type="button" onClick={handleSave}>
          {labels?.save ?? 'Save preferences'}
        </button>
      </div>
    </section>
  )
}
