import { createContext, useContext, useState, type ReactNode } from "react";
import { createPortal } from "react-dom";
import PageScroller from "./PageScroller";

// The DOM node a module's toolbar portals into (see PageToolbar below). PageShell provides its
// fixed band as the default target; a module can override it with its own <PageToolbarSlot.Provider>
// to redirect its toolbar somewhere else — the channel page points it at a sticky sub-header so the
// toolbar rides with the tabs instead of the shell's top band.
export const PageToolbarSlot = createContext<HTMLElement | null>(null);

// A module renders its per-module fixed band (filter chips / sort / counts) through this. The
// content lands in the shell's toolbar band — ABOVE the scroller, so it stays put while the content
// scrolls and is never touched by the scroller's edge-fade mask. It's a portal, so the toolbar's
// event handlers and closures still belong to the module's React subtree (setFilters etc. work as
// if it were rendered inline). Renders nothing until the band mounts (one tick on first mount) and
// nothing when there's no shell — so a module works with or without one.
export function PageToolbar({ children }: { children: ReactNode }) {
  const slot = useContext(PageToolbarSlot);
  return slot ? createPortal(children, slot) : null;
}

// The page shell: the fixed chrome around a single scrollable content region. It formalizes "what
// is fixed vs what scrolls" per module, so the chrome App used to hand-assemble around every page
// (a floating Header + a top-padded wrapper + PageScroller, plus the channel page's separate
// branch that rendered its OWN scroller) collapses to one contract and one scroller.
//
// Regions, top to bottom:
//  - `header`  — floats (absolute, height `var(--hdr-h)`) over the content column; the wrapper pads
//                its top by that height so nothing hides under it. Omit it (channel page) → no pad
//                and no fixed top band, so the module's own banner can scroll from the very top.
//  - `banners` — in-flow, fixed notices (demo/version) above the scroll region.
//  - toolbar band — in-flow, FIXED per-module band (filter chips / sort / counts); filled by a
//                module via <PageToolbar>. Empty ⇒ zero height, so pages without one lose nothing.
//  - children  — the ONE scrollable region. `PageScroller` owns the scroller and its two
//                invariants (`scrollbar-gutter: stable`, edge fade); see that file.
//
// The shell renders a fragment so it drops straight into App's `relative` content column — the
// floating header keeps resolving its `absolute` position against that same ancestor.
export default function PageShell({
  header,
  banners,
  children,
  scrollKey,
  fadeTop = true,
}: {
  header?: ReactNode;
  banners?: ReactNode;
  children: ReactNode;
  // Identifies the logical view for the scroll restore/reset policy — see PageScroller.
  scrollKey?: string;
  // Off when this page's scroll region starts with a sticky header — see PageScroller.
  fadeTop?: boolean;
}) {
  const [toolbarSlot, setToolbarSlot] = useState<HTMLDivElement | null>(null);
  return (
    <PageToolbarSlot.Provider value={toolbarSlot}>
      {header}
      <div className={`flex-1 min-w-0 min-h-0 flex flex-col ${header ? "pt-[var(--hdr-h)]" : ""}`}>
        {banners}
        <div ref={setToolbarSlot} className="shrink-0" />
        <PageScroller scrollKey={scrollKey} fadeTop={fadeTop}>
          {children}
        </PageScroller>
      </div>
    </PageToolbarSlot.Provider>
  );
}
