// CookieConsentBanner.jsx — v0.13.0 / roadmap item #11 / SPEC §14.5.
//
// A non-modal bottom-of-page banner that asks the user once which
// categories of cookies they accept. The framework's strictly-necessary
// cookies (session, signed payloads) are always on; the user can opt in
// or out of analytics (which gates the §13 SDK landing in v0.15.0) and
// "other" (third-party embeds, social widgets if a deployment adds any).
//
// Visible until the user makes a choice. Hides itself once the choice
// is recorded. The /settings/notifications "Privacy & cookies" tab
// surfaces the current choice and re-opens the banner via `forceOpen`.

import { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import { getConsent, setConsent, hasChosen, hydrateFromServer } from '../lib/consent.js'
import { getCookieConsent, setCookieConsent } from '../api.js'

const CATEGORIES = [
  {
    key: 'essential-only',
    label: 'Essential only',
    description: 'Just the cookies the app needs to keep you signed in and protect submissions. (Sign-in session, signed payloads.)',
    flags: { analytics: false, other: false },
  },
  {
    key: 'essential-analytics',
    label: 'Essential + analytics',
    description: 'Adds anonymous usage analytics so the framework can see which surfaces get used. No third-party scripts beyond the analytics SDK.',
    flags: { analytics: true, other: false },
  },
  {
    key: 'essential-analytics-other',
    label: 'Essential + analytics + other',
    description: 'Adds analytics plus any third-party embeds the deployment configures (e.g. social widgets). Choose this if you want the full surface.',
    flags: { analytics: true, other: true },
  },
]

function selectionKeyFor(consent) {
  if (consent.analytics && consent.other) return 'essential-analytics-other'
  if (consent.analytics && !consent.other) return 'essential-analytics'
  return 'essential-only'
}

export default function CookieConsentBanner({ viewer, forceOpen, onClosed }) {
  const [open, setOpen] = useState(() => forceOpen || !hasChosen())
  const [choice, setChoice] = useState(() => selectionKeyFor(getConsent()))
  const [saving, setSaving] = useState(false)
  const [error, setError] = useState(null)

  // When forceOpen flips (settings "Change" affordance), re-render the
  // banner and pre-select the user's current choice.
  useEffect(() => {
    if (forceOpen) {
      setOpen(true)
      setChoice(selectionKeyFor(getConsent()))
    }
  }, [forceOpen])

  // Server-side hydrate for authenticated viewers per the v0.13.0
  // precedence rule: a server row overrides local; absent server row,
  // upload the local choice.
  useEffect(() => {
    if (!viewer?.user_id) return
    let cancelled = false
    getCookieConsent()
      .then(record => {
        if (cancelled) return
        if (record.recorded_at) {
          // Server is authoritative — adopt + hide the banner unless
          // the settings page forced it open.
          hydrateFromServer(record)
          setChoice(selectionKeyFor(record))
          if (!forceOpen) setOpen(false)
        } else if (hasChosen()) {
          // Local has a choice the server doesn't know about yet — push.
          const local = getConsent()
          setCookieConsent({ analytics: local.analytics, other: local.other })
            .then(r => hydrateFromServer(r))
            .catch(() => {})
        }
      })
      .catch(() => {
        // Network or auth error — leave the local-only path in place.
      })
    return () => { cancelled = true }
  }, [viewer?.user_id])  // eslint-disable-line react-hooks/exhaustive-deps

  if (!open) return null

  async function save() {
    setSaving(true)
    setError(null)
    const picked = CATEGORIES.find(c => c.key === choice) || CATEGORIES[0]
    try {
      setConsent(picked.flags)
      if (viewer?.user_id) {
        // Best-effort server persistence. A failure here doesn't
        // invalidate the local choice; the banner still hides because
        // the user expressed their preference. The server can catch up
        // on the next sign-in via the hydrate path above.
        try {
          const r = await setCookieConsent(picked.flags)
          hydrateFromServer(r)
        } catch (e) {
          setError(`Saved locally; server sync failed (${e.message}).`)
        }
      }
      setOpen(false)
      onClosed?.()
    } finally {
      setSaving(false)
    }
  }

  return (
    <div className="cookie-consent-banner" role="region" aria-label="Cookie consent">
      <div className="cookie-consent-body">
        <h2 className="cookie-consent-title">Cookies &amp; privacy</h2>
        <p className="cookie-consent-intro">
          This site uses cookies. Essential cookies keep you signed in and
          protect your submissions; analytics and other cookies are
          optional. Choose what you allow — you can change this any time
          from <Link to="/settings/notifications">Settings &rarr; Privacy &amp; cookies</Link>.
        </p>
        <fieldset className="cookie-consent-choices">
          <legend className="visually-hidden">Cookie categories</legend>
          {CATEGORIES.map(c => (
            <label key={c.key} className={`cookie-consent-choice ${choice === c.key ? 'is-selected' : ''}`}>
              <input
                type="radio"
                name="cookie-consent-choice"
                value={c.key}
                checked={choice === c.key}
                onChange={() => setChoice(c.key)}
                disabled={saving}
              />
              <span className="cookie-consent-choice-text">
                <span className="cookie-consent-choice-label">{c.label}</span>
                <span className="cookie-consent-choice-desc">{c.description}</span>
              </span>
            </label>
          ))}
        </fieldset>
        <p className="cookie-consent-links">
          <Link to="/cookies">Cookies policy</Link>
          <span aria-hidden> &middot; </span>
          <Link to="/privacy">Privacy policy</Link>
        </p>
        {error && <p className="cookie-consent-error">{error}</p>}
        <div className="cookie-consent-actions">
          <button
            type="button"
            className="btn-primary"
            onClick={save}
            disabled={saving}
          >
            {saving ? 'Saving…' : 'Save choice'}
          </button>
        </div>
      </div>
    </div>
  )
}
