/* shell.jsx — shared app chrome for every Advokacy v2 feature page.
   Each nav item is a real, separate URL served by the Express app
   (see server.js: /ask, /documents, /diary). */
const { useState, useEffect, useRef } = React;

/* Clean per-feature routes (wired in server.js). */
const PAGES = {
  ask:   "/ask",
  docs:  "/documents",
  diary: "/diary",
  login: "/login",
};

/* ---- auth client ----
   Email+password sessions backed by /api/auth (see netlify/functions/auth.js).
   The token + a cached public profile live in localStorage; the token is sent as
   a Bearer header on protected calls. Ask is free; Document Intelligence (and the
   Case Diary backend) require a session. A `adv-auth` window event fires on any
   change so the chrome re-renders. */
const AUTH_TOKEN_KEY = "adv_v2_auth_token";
const AUTH_USER_KEY  = "adv_v2_auth_user";
const AdvAuth = {
  token(){ try{ return localStorage.getItem(AUTH_TOKEN_KEY) || ""; }catch(e){ return ""; } },
  user(){ try{ return JSON.parse(localStorage.getItem(AUTH_USER_KEY) || "null"); }catch(e){ return null; } },
  isLoggedIn(){ return !!this.token() && !!this.user(); },
  authHeader(){ const t=this.token(); return t ? { Authorization: "Bearer " + t } : {}; },
  _emit(){ try{ window.dispatchEvent(new Event("adv-auth")); }catch(e){} },
  _set(token, user){
    try{
      if(token) localStorage.setItem(AUTH_TOKEN_KEY, token);
      if(user)  localStorage.setItem(AUTH_USER_KEY, JSON.stringify(user));
    }catch(e){}
    this._emit();
  },
  _clear(){ try{ localStorage.removeItem(AUTH_TOKEN_KEY); localStorage.removeItem(AUTH_USER_KEY); }catch(e){} this._emit(); },
  async _post(action, body, withAuth){
    const r=await fetch("/api/auth",{ method:"POST",
      headers:{ "Content-Type":"application/json", ...(withAuth?this.authHeader():{}) },
      body: JSON.stringify({ action, ...(body||{}) }) });
    let d=null; try{ d=await r.json(); }catch(e){}
    return { r, d };
  },
  async login(email, password){
    const { r, d }=await this._post("login", { email, password });
    if(!r.ok || !d || !d.ok) throw new Error((d&&d.error) || "Login failed.");
    this._set(d.token, d.user); return d.user;
  },
  async signup(p){
    const { r, d }=await this._post("signup", p);
    if(!r.ok || !d || !d.ok) throw new Error((d&&d.error) || "Sign up failed.");
    this._set(d.token, d.user); return d.user;
  },
  async logout(){ try{ await this._post("logout", {}, true); }catch(e){} this._clear(); },
  // Verify the cached token is still valid (sessions are cleared on server
  // restart). Returns the fresh user, or null if signed out.
  async refresh(){
    if(!this.token()) return null;
    try{
      const { r, d }=await this._post("me", {}, true);
      if(r.ok && d && d.ok){ this._set(null, d.user); return d.user; }
      if(r.status===401){ this._clear(); return null; }
    }catch(e){}
    return this.user();
  },
};
window.AdvAuth = AdvAuth;

/* ---- session search history ----
   Temporary, session-scoped record of the user's searches. Backed by
   sessionStorage so it survives reloads but is cleared when the tab/session
   ends. The full result is stored, so reopening a search is instant (no
   re-fetch). Other components subscribe via the "adv-history" window event. */
const HIST_KEY = "adv_v2_history";
const HIST_MAX = 12;
function histRead(){ try{ return JSON.parse(sessionStorage.getItem(HIST_KEY) || "[]"); }catch(e){ return []; } }
function histWrite(list){
  try{ sessionStorage.setItem(HIST_KEY, JSON.stringify(list)); }catch(e){}
  try{ window.dispatchEvent(new CustomEvent("adv-history")); }catch(e){}
}
const AdvHistory = {
  list(){ return histRead(); },
  get(id){ return histRead().find(e => e.id === id) || null; },
  add(entry){
    if(!entry || !entry.id) return entry;
    const q = String(entry.query || "").trim().toLowerCase();
    // de-dupe by id and by identical query text, newest first
    const list = histRead().filter(e => e.id !== entry.id && String(e.query||"").trim().toLowerCase() !== q);
    list.unshift(entry);
    histWrite(list.slice(0, HIST_MAX));
    return entry;
  },
  remove(id){ histWrite(histRead().filter(e => e.id !== id)); },
  clear(){ histWrite([]); },
};
function relTime(ts){
  const s = Math.max(0, Math.floor((Date.now() - (ts||0)) / 1000));
  if(s < 45) return "Just now";
  if(s < 3600) return Math.floor(s/60) + "m ago";
  if(s < 86400) return Math.floor(s/3600) + "h ago";
  return Math.floor(s/86400) + "d ago";
}
window.AdvHistory = AdvHistory;

/* ---- icon set (simple line icons) ---- */
const ICONS = {
  ask:   "M12 3a9 9 0 0 0-9 9 8.8 8.8 0 0 0 1.5 4.9L3 21l4.3-1.4A9 9 0 1 0 12 3Z",
  docs:  "M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5ZM14 3v5h5M9 13h6M9 17h6",
  diary: "M4 5a2 2 0 0 1 2-2h11a1 1 0 0 1 1 1v15a1 1 0 0 1-1 1H6a2 2 0 0 1-2-2ZM8 3v18M18 7h2M18 12h2M18 17h2",
  scale: "M12 3v18M7 7h10M5 7l-2.5 6a3.5 3.5 0 0 0 7 0L7 7Zm12 0-2.5 6a3.5 3.5 0 0 0 7 0L17 7ZM7 21h10",
  search:"M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14ZM20 20l-3.5-3.5",
  arrow: "M5 12h14M13 6l6 6-6 6",
  up:    "M12 19V5M6 11l6-6 6 6",
  check: "M20 6 9 17l-5-5",
  shield:"M12 3l7 3v6c0 4.5-3 7.5-7 9-4-1.5-7-4.5-7-9V6l7-3Z",
  ext:   "M14 4h6v6M20 4l-9 9M18 13v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5",
  globe: "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18ZM3 12h18M12 3c2.5 2.5 3.5 6 3.5 9s-1 6.5-3.5 9c-2.5-2.5-3.5-6-3.5-9s1-6.5 3.5-9Z",
  copy:  "M9 9h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V10a1 1 0 0 1 1-1ZM5 15H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1",
  share: "M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7M16 6l-4-4-4 4M12 2v13",
  book:  "M4 5a2 2 0 0 1 2-2h12v16H6a2 2 0 0 0-2 2ZM4 19a2 2 0 0 0 2 2h12",
  spark: "M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8L12 3Z",
  filter:"M3 5h18M6 12h12M10 19h4",
  plus:  "M12 5v14M5 12h14",
  upload:"M12 16V4M7 9l5-5 5 5M5 20h14",
  clock: "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18ZM12 7v5l3 2",
  chev:  "M9 6l6 6-6 6",
  cog:   "M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6ZM19.4 13a1.7 1.7 0 0 0 .3 1.9l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-2.9 1.2V21a2 2 0 0 1-4 0v-.1a1.7 1.7 0 0 0-2.9-1.2l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0-1.2-2.9H1a2 2 0 0 1 0-4h.1a1.7 1.7 0 0 0 1.2-2.9l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.9.3 1.7 1.7 0 0 0 1-1.6V3a2 2 0 0 1 4 0v.1a1.7 1.7 0 0 0 2.9 1.2l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0 1.6 1H21a2 2 0 0 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z",
  panel: "M4 4h16a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1ZM9 4v16",
  branch:"M6 3v12M6 15a3 3 0 1 0 0 6 3 3 0 0 0 0-6M18 3a3 3 0 1 0 0 6 3 3 0 0 0 0-6M18 9a9 9 0 0 1-9 9",
};
function Icon({ name, style }){
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7"
         strokeLinecap="round" strokeLinejoin="round" style={style}>
      <path d={ICONS[name] || ""} />
    </svg>
  );
}

const NAV = [
  { id:"ask",   icon:"ask",   label:"Ask Advokacy",          href:PAGES.ask,   group:"Research" },
  { id:"docs",  icon:"docs",  label:"Document Intelligence",  href:PAGES.docs,  group:"Research" },
  { id:"diary", icon:"diary", label:"Case Diary",            href:PAGES.diary, group:"Workspace", badge:"3" },
];

function Sidebar({ active }){
  // Live session search history (falls back to the sample recents when empty).
  const [hist, setHist] = useState(() => AdvHistory.list());
  useEffect(() => {
    const h = () => setHist(AdvHistory.list());
    window.addEventListener("adv-history", h);
    window.addEventListener("storage", h);
    return () => { window.removeEventListener("adv-history", h); window.removeEventListener("storage", h); };
  }, []);
  const recents = hist.length
    ? hist.map(e => ({ label: e.label || e.query, area: e.area || "", when: relTime(e.ts), href: PAGES.ask + "?h=" + encodeURIComponent(e.id) }))
    : (window.RECENTS || []).map(r => ({ label: r.label, area: r.area, when: r.when, href: PAGES.ask + "?q=" + r.key }));

  // "New question" / "Ask Advokacy" open a fresh search tab. On the Ask page an
  // in-page handler does it without reloading; from other pages we navigate to
  // /ask?new=1, which opens a new tab on arrival.
  const askNew = (e) => {
    if (typeof window.__advNewTab === "function") {
      e.preventDefault(); window.__advNewTab();
      if (window.innerWidth <= 880 && typeof window.__advCloseRail === "function") window.__advCloseRail();
    }
  };

  let lastGroup = null;
  return (
    <aside className="rail">
      <a className="rail-top" href="/" title="Go to homepage">
        <div className="brand-mark">A</div>
        <div>
          <div className="brand-name">Advok<b>acy</b></div>
          <div className="brand-tag">Legal Intelligence</div>
        </div>
      </a>
      <nav className="nav">
        {NAV.map(n=>{
          const head = n.group!==lastGroup ? (lastGroup=n.group, <div className="nav-lbl" key={"g"+n.id}>{n.group}</div>) : null;
          const isAsk = n.id==="ask";
          return (
            <React.Fragment key={n.id}>
              {head}
              <a className={"nav-item"+(active===n.id?" on":"")} href={isAsk?PAGES.ask+"?new=1":n.href}
                 onClick={isAsk?askNew:undefined}>
                <span className="ni-ico"><Icon name={n.icon}/></span>
                {n.label}
                {n.badge && <span className="badge">{n.badge}</span>}
              </a>
            </React.Fragment>
          );
        })}
      </nav>
      <div className="rail-recents">
        <div className="nav-lbl rail-recents-head" style={{paddingTop:18}}>
          <span>{hist.length ? "This session" : "Recent research"}</span>
          {hist.length ? <button className="rail-clear" onClick={()=>AdvHistory.clear()} title="Clear session searches">Clear</button> : null}
        </div>
        {recents.map((r,i)=>(
          <a className="recent" key={i} href={r.href}>
            <div className="rc-t">{r.label}</div>
            <div className="rc-m">{r.area ? <><span>{r.area}</span><span>·</span></> : null}<span>{r.when}</span></div>
          </a>
        ))}
      </div>
      <div className="rail-user">
        <div className="avatar">AK</div>
        <div style={{minWidth:0}}>
          <div className="ru-name">Adv. A. Khanna</div>
          <div className="ru-mail">Chambers · Delhi HC</div>
        </div>
      </div>
    </aside>
  );
}

/* ---- collapsible sidebar toggle (lives in the topbar so it stays visible) ---- */
const RAIL_KEY = "adv_v2_rail";
// Default: collapsed on small screens (where the rail is an overlay), open on
// desktop — unless the user has set an explicit preference.
function railCollapsed(){
  try{
    const p = localStorage.getItem(RAIL_KEY);
    if (p === "1") return true;
    if (p === "0") return false;
    return window.innerWidth <= 880;
  }catch(e){ return false; }
}
function applyRail(c){ try{ document.documentElement.setAttribute("data-rail", c ? "collapsed" : "open"); }catch(e){} }
function RailToggle(){
  const [c,setC]=useState(railCollapsed);
  useEffect(()=>{ applyRail(c); try{ localStorage.setItem(RAIL_KEY, c ? "1" : "0"); }catch(e){} },[c]);
  // expose a close helper so in-page actions (e.g. "Ask Advokacy" opening a tab)
  // can dismiss the mobile overlay without navigating
  const closeRef=useRef(); closeRef.current=()=>setC(true);
  useEffect(()=>{ window.__advCloseRail=()=>closeRef.current&&closeRef.current();
    return ()=>{ if(window.__advCloseRail) delete window.__advCloseRail; }; },[]);
  return (
    <>
      <button className="tb-toggle" onClick={()=>setC(v=>!v)}
              aria-label={c?"Open sidebar":"Close sidebar"} title={c?"Open sidebar":"Close sidebar"}>
        <Icon name="panel"/>
      </button>
      {/* tap-to-close scrim behind the rail (mobile overlay only; hidden on desktop) */}
      <div className="rail-scrim" onClick={()=>setC(true)} aria-hidden="true"/>
    </>
  );
}

function Topbar({ crumb, right }){
  return (
    <header className="topbar">
      <RailToggle/>
      <div className="tb-crumb">
        <b>Advokacy</b>
        {crumb && <><span className="sep">/</span><span>{crumb}</span></>}
      </div>
      <div className="tb-spacer"/>
      {right}
      <AccountMenu/>
      <SettingsMenu/>
      <div className="tb-chip ok"><Icon name="shield"/> Grounded in verified case law</div>
    </header>
  );
}

// Topbar account control: a "Log in" button when signed out, or the user's name
// with a Log-out menu when signed in. Re-validates the cached session on mount.
function AccountMenu(){
  const [user, setUser] = useState(() => AdvAuth.user());
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    const h = () => setUser(AdvAuth.user());
    window.addEventListener("adv-auth", h);
    window.addEventListener("storage", h);
    AdvAuth.refresh().then(u => setUser(u));
    return () => { window.removeEventListener("adv-auth", h); window.removeEventListener("storage", h); };
  }, []);
  useEffect(() => {
    function onDoc(e){ if(ref.current && !ref.current.contains(e.target)) setOpen(false); }
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, []);
  if(!user){
    const next = encodeURIComponent(location.pathname + location.search);
    return <a className="btn btn-ghost tb-login" href={PAGES.login + "?next=" + next}>Log in</a>;
  }
  const name = user.full_name || user.email || "Account";
  const initial = (name.trim()[0] || "A").toUpperCase();
  return (
    <div className="acct" ref={ref}>
      <button className="acct-btn" onClick={() => setOpen(o=>!o)} title={user.email}>
        <span className="acct-av">{initial}</span>
        <span className="acct-name">{name}</span>
        <Icon name="chev" style={{width:13,height:13,transform:"rotate(90deg)",opacity:.6}}/>
      </button>
      {open && (
        <div className="acct-menu">
          <div className="acct-id">
            <div className="acct-id-n">{name}</div>
            <div className="acct-id-e mono">{user.email}</div>
          </div>
          {user.is_admin && <a className="acct-item" href="/admin">Admin dashboard</a>}
          <button className="acct-item" onClick={() => { AdvAuth.logout().then(() => location.reload()); }}>Log out</button>
        </div>
      )}
    </div>
  );
}

function AppShell({ active, crumb, topRight, children }){
  return (
    <div className="app">
      <Sidebar active={active}/>
      <div className="main">
        <Topbar crumb={crumb} right={topRight}/>
        {children}
      </div>
    </div>
  );
}

/* ---- product settings: accent / density / dark mode ----
   The prototype shipped these as an authoring "Tweaks" panel; per the v2
   handoff that panel is NOT shipped. Instead they become a real product
   setting: a small popover in the topbar, persisted to localStorage and
   applied to the document root (also applied pre-paint by the inline boot
   script in each HTML to avoid a flash). */
const ACCENTS = [
  { name:"indigo", hue:264, hex:"#4f46e5" },
  { name:"teal",   hue:196, hex:"#0d9488" },
  { name:"plum",   hue:328, hex:"#a021c4" },
  { name:"slate",  hue:262, hex:"#5b6478" },
];
const SETTINGS_KEY = "adv_v2_settings";
const SETTINGS_DEFAULTS = { accent:"indigo", density:"regular", dark:false };

function readSettings(){
  try{ return Object.assign({}, SETTINGS_DEFAULTS, JSON.parse(localStorage.getItem(SETTINGS_KEY)||"{}")); }
  catch(e){ return Object.assign({}, SETTINGS_DEFAULTS); }
}
function applySettings(s){
  const r=document.documentElement;
  const a=ACCENTS.find(x=>x.name===s.accent)||ACCENTS[0];
  r.style.setProperty("--brand-h", a.hue);
  if(a.name==="slate"){
    r.style.setProperty("--brand","oklch(0.45 0.045 "+a.hue+")");
    r.style.setProperty("--brand-ink","oklch(0.38 0.05 "+a.hue+")");
    r.style.setProperty("--brand-soft","oklch(0.955 0.012 "+a.hue+")");
    r.style.setProperty("--brand-line","oklch(0.86 0.02 "+a.hue+")");
  } else {
    ["--brand","--brand-ink","--brand-soft","--brand-line"].forEach(p=>r.style.removeProperty(p));
  }
  r.setAttribute("data-density", s.density);
  r.setAttribute("data-theme", s.dark?"dark":"light");
}
/* Apply saved settings as early as the script runs (the HTML boot script
   already set root attributes pre-paint; this keeps React state in sync). */
function useSettings(){
  const [s,setS]=useState(readSettings);
  useEffect(()=>{ applySettings(s); try{ localStorage.setItem(SETTINGS_KEY, JSON.stringify(s)); }catch(e){} },[s.accent,s.density,s.dark]);
  const set=(k,v)=>setS(prev=>({ ...prev, [k]:v }));
  return [s,set];
}

function SettingsMenu(){
  const [s,set]=useSettings();
  const [open,setOpen]=useState(false);
  const ref=useRef(null);
  useEffect(()=>{
    function onDoc(e){ if(ref.current && !ref.current.contains(e.target)) setOpen(false); }
    function onEsc(e){ if(e.key==="Escape") setOpen(false); }
    document.addEventListener("mousedown",onDoc);
    window.addEventListener("keydown",onEsc);
    return ()=>{ document.removeEventListener("mousedown",onDoc); window.removeEventListener("keydown",onEsc); };
  },[]);
  return (
    <div className="set" ref={ref}>
      <button className="set-btn" title="Display settings" aria-label="Display settings"
              aria-expanded={open} onClick={()=>setOpen(o=>!o)}>
        <Icon name="cog"/>
      </button>
      {open && (
        <div className="set-menu" role="menu">
          <div className="set-row">
            <div className="set-lbl">Accent</div>
            <div className="set-swatches">
              {ACCENTS.map(a=>(
                <button key={a.name} className={"set-sw"+(s.accent===a.name?" on":"")}
                        style={{background:a.hex}} title={a.name} aria-label={a.name}
                        onClick={()=>set("accent",a.name)}/>
              ))}
            </div>
          </div>
          <div className="set-row">
            <div className="set-lbl">Density</div>
            <div className="set-seg">
              {["compact","regular","comfy"].map(d=>(
                <button key={d} className={s.density===d?"on":""} onClick={()=>set("density",d)}>{d}</button>
              ))}
            </div>
          </div>
          <div className="set-row set-toggle">
            <span>Dark mode</span>
            <button className={"sw"+(s.dark?" on":"")} role="switch" aria-checked={s.dark}
                    aria-label="Dark mode" onClick={()=>set("dark",!s.dark)}><i/></button>
          </div>
        </div>
      )}
    </div>
  );
}

/* ---- tiny toast helper (used by copy / share / export actions) ---- */
function toast(msg){
  let el=document.getElementById("adv-toast");
  if(!el){ el=document.createElement("div"); el.id="adv-toast"; el.className="toast"; document.body.appendChild(el); }
  el.textContent=msg; el.classList.add("on");
  clearTimeout(toast._t); toast._t=setTimeout(()=>el.classList.remove("on"),2000);
}

Object.assign(window, { Icon, ICONS, AppShell, Sidebar, Topbar, PAGES, SettingsMenu, AccountMenu, AdvAuth, applySettings, readSettings, toast });
