import { useEffect, useState } from 'react'
import { Routes, Route, Link, useNavigate } from 'react-router-dom'
import { getMe, subscribeToNotifications } from './api'
import Catalog from './components/Catalog.jsx'
import Inbox from './components/Inbox.jsx'
import RFCView from './components/RFCView.jsx'
import PRView from './components/PRView.jsx'
import ProposalView from './components/ProposalView.jsx'
import ProposeModal from './components/ProposeModal.jsx'
import Landing from './components/Landing.jsx'
import Login from './components/Login.jsx'
import BetaPending from './components/BetaPending.jsx'
import Philosophy from './components/Philosophy.jsx'
import NotificationSettings from './components/NotificationSettings.jsx'
import Admin from './components/Admin.jsx'
import ToastHost, { showToast } from './components/ToastHost.jsx'
import CookieConsentBanner from './components/CookieConsentBanner.jsx'
import Privacy from './pages/Privacy.jsx'
import Cookies from './pages/Cookies.jsx'
import './App.css'

export default function App() {
  const [me, setMe] = useState(null)
  const [loading, setLoading] = useState(true)
  const [proposeOpen, setProposeOpen] = useState(false)
  const [catalogVersion, setCatalogVersion] = useState(0)
  const [inboxOpen, setInboxOpen] = useState(false)
  const [unreadCount, setUnreadCount] = useState(0)
  const [inboxTick, setInboxTick] = useState(0)
  // §14.5: a tick that, when bumped, asks <CookieConsentBanner> to
  // re-open even if the user has already made a choice. The settings
  // "Privacy & cookies" tab dispatches a `rfc-app:cookie-consent-reopen`
  // event that bumps this.
  const [consentReopenTick, setConsentReopenTick] = useState(0)
  const navigate = useNavigate()

  useEffect(() => {
    const handler = () => setConsentReopenTick(t => t + 1)
    window.addEventListener('rfc-app:cookie-consent-reopen', handler)
    return () => window.removeEventListener('rfc-app:cookie-consent-reopen', handler)
  }, [])

  useEffect(() => {
    getMe()
      .then(setMe)
      .catch(() => setMe({ authenticated: false }))
      .finally(() => setLoading(false))
  }, [])

  // §15.3 — subscribe to the live SSE stream for authenticated viewers
  // so the badge counter and the toast surface stay in lockstep with
  // the inbox. Tabs that miss an event because they were closed pick
  // it up on next sign-in via the snapshot frame.
  useEffect(() => {
    if (!me?.authenticated) return undefined
    const close = subscribeToNotifications({
      onSnapshot: payload => setUnreadCount(payload.unread_count || 0),
      onNotification: payload => {
        setUnreadCount(c => c + 1)
        setInboxTick(t => t + 1)
        // §15.3: personal-direct events get a toast even when the user
        // isn't on the relevant view — they're the named subject.
        // Churn never toasts; structural toasts only when it lands on
        // a slug the user is currently viewing (URL match).
        const isPersonal = payload.category === 'personal-direct'
        const onCurrentSlug = payload.rfc_slug && window.location.pathname.includes(`/rfc/${payload.rfc_slug}`)
        if (isPersonal || onCurrentSlug) {
          showToast({
            summary: payload.summary,
            category: payload.category,
            link: payload.rfc_slug ? `/rfc/${payload.rfc_slug}` : null,
          })
        }
      },
      onRead: () => {
        setUnreadCount(c => Math.max(0, c - 1))
        setInboxTick(t => t + 1)
      },
    })
    return close
  }, [me?.authenticated])

  if (loading) {
    return <div className="boot">Loading…</div>
  }

  // The deployment is in private beta: anonymous visitors get the full
  // app in read-only mode (viewer = null is passed through to every
  // component), and write affordances are hidden at the component
  // level. /beta-pending is the post-OAuth-rejection page reachable by
  // anyone. The original §14.1 Landing surface is retained for the
  // `/welcome` URL only, in case a deployment wants to link to it.
  const viewer = me?.authenticated ? me.user : null
  const isAdmin = viewer && (viewer.role === 'owner' || viewer.role === 'admin')

  return (
    <div className="app">
      <header className="app-header">
        <div className="app-brand">
          <Link to="/">{import.meta.env.VITE_APP_NAME}</Link>
        </div>
        <div className="header-right">
          {/* §14.3: the persistent About link. One word, no badge, no
              state — visible from every screen so a viewer mid-PR who
              wonders why a conversation is public can reach the answer
              in two clicks. Anonymous viewers see it too. */}
          <Link to="/philosophy" className="header-about" title="Why this exists (§14)">
            About
          </Link>
          {viewer && (
            <Link to="/settings/notifications" className="header-settings" title="Notification settings (§15)">
              Settings
            </Link>
          )}
          {isAdmin && (
            <Link to="/admin" className="header-admin" title="Admin home base">
              Admin
            </Link>
          )}
          {viewer && (
            <button
              className="inbox-trigger"
              onClick={() => setInboxOpen(o => !o)}
              title="Notifications inbox (§15.2)"
            >
              <span aria-hidden>📮</span>
              {unreadCount > 0 && (
                <span className="badge">{unreadCount > 99 ? '99+' : unreadCount}</span>
              )}
            </button>
          )}
          {viewer ? (
            <>
              <span className="user-name">{viewer.display_name}</span>
              <span className={`user-role-badge role-${viewer.role}`}>{viewer.role}</span>
              <a className="btn-link" href="/auth/logout">Sign out</a>
            </>
          ) : (
            <Link className="btn-signin-header" to="/login" title="Private beta — only invited emails can sign in">
              Sign in <span className="beta-chip">Beta</span>
            </Link>
          )}
        </div>
      </header>
      <div className="app-body">
        <Routes>
          <Route path="/welcome" element={<Landing />} />
          <Route path="/login" element={<Login />} />
          <Route path="/beta-pending" element={<BetaPending />} />
          <Route path="/philosophy" element={<PhilosophyWithSidebar viewer={viewer} />} />
          {/* §14.5 / §14.6: cookie-consent companions to /philosophy.
              Available to anonymous and authenticated viewers alike. */}
          <Route path="/privacy" element={<PolicyShell><Privacy /></PolicyShell>} />
          <Route path="/cookies" element={<PolicyShell><Cookies /></PolicyShell>} />
          {viewer && (
            <Route path="/settings/notifications" element={<NotificationSettingsWithSidebar viewer={viewer} />} />
          )}
          {isAdmin && (
            <Route path="/admin/*" element={<AdminWithSidebar viewer={viewer} />} />
          )}
          <Route path="*" element={
            <>
              <Catalog
                viewer={viewer}
                onProposeRFC={() => setProposeOpen(true)}
                version={catalogVersion}
              />
              <main className="main-pane">
                <Routes>
                  <Route path="/" element={<Welcome viewer={viewer} />} />
                  <Route path="/rfc/:slug" element={<RFCView viewer={viewer} />} />
                  <Route path="/rfc/:slug/pr/:prNumber" element={<PRView viewer={viewer} />} />
                  <Route path="/proposals/:prNumber" element={<ProposalView viewer={viewer} onChange={() => setCatalogVersion(v => v + 1)} />} />
                </Routes>
              </main>
            </>
          } />
        </Routes>
      </div>
      {proposeOpen && viewer && (
        <ProposeModal
          viewer={viewer}
          onClose={() => setProposeOpen(false)}
          onSubmitted={({ pr_number }) => {
            setProposeOpen(false)
            setCatalogVersion(v => v + 1)
            navigate(`/proposals/${pr_number}`)
          }}
        />
      )}
      {inboxOpen && viewer && (
        <Inbox onClose={() => setInboxOpen(false)} lastChangeTick={inboxTick} />
      )}
      <ToastHost />
      <CookieConsentBanner viewer={viewer} forceOpen={consentReopenTick} />
    </div>
  )
}

function PolicyShell({ children }) {
  // §14.5 / §14.6 policy pages reuse the chrome-pane shape so they
  // render full-width without the catalog rail. The components inside
  // carry their own back affordance per Philosophy.jsx's pattern.
  return <main className="chrome-pane">{children}</main>
}

function PhilosophyWithSidebar({ viewer }) {
  // The chrome surfaces (§14.2 philosophy, §15 settings, §6/§17 admin)
  // all use the full app body — no catalog left pane, no propose modal.
  // The header carries the navigation back; the body is a single
  // reading surface.
  return (
    <main className="chrome-pane">
      <Philosophy authenticated={!!viewer} />
    </main>
  )
}

function NotificationSettingsWithSidebar({ viewer }) {
  return (
    <main className="chrome-pane">
      <NotificationSettings viewer={viewer} />
    </main>
  )
}

function AdminWithSidebar({ viewer }) {
  return (
    <main className="chrome-pane">
      <Admin viewer={viewer} />
    </main>
  )
}

function Welcome({ viewer }) {
  if (!viewer) {
    return (
      <div className="welcome">
        <h1>Welcome.</h1>
        <p>
          The catalog on the left lists every super-draft and active RFC in the
          framework. Open one to read the canonical body and the public
          conversation behind each definition.
        </p>
        <p>
          Discussion and contribution are in private <strong>Beta</strong> —
          read freely, and <Link to="/login">sign in</Link> if your email has
          been invited.
        </p>
        <p>
          Wondering why a conversation is public, why graduation costs what it
          does, or why the model is in the chat? <Link to="/philosophy">Read the
          philosophy</Link>.
        </p>
      </div>
    )
  }
  return (
    <div className="welcome">
      <h1>Welcome, {viewer.display_name}.</h1>
      <p>
        The catalog on the left lists every super-draft and active RFC in the
        framework. Open one to read the canonical body, or use{' '}
        <strong>Propose New RFC</strong> at the bottom of the catalog to open an
        idea PR against the meta repository.
      </p>
      <p>
        Wondering why a conversation is public, why graduation costs what it
        does, or why the model is in the chat? <Link to="/philosophy">Read the
        philosophy</Link>.
      </p>
    </div>
  )
}
