// App.jsx — Root application component
// Wires together: hash router, login guard, shell layout, and lazy screen container.
//
// Architecture:
//   1. Router bootstrap — registers all valid routes, sets default, inits hash listener.
//      Must complete before ReactDOM.createRoot so router.current() is correct when
//      App's useState initialiser fires.
//   2. Login guard — if authState.status !== "authed", renders <Login> + TweaksPanel
//      demo shortcut only.  While status === "loading" a spinner/skeleton is shown.
//   3. Shell layout — once authed, renders <Sidebar> + <TopBar> + screen container.
//   4. Lazy screen container — renders only the active screen component based on
//      `route` state.  Each screen is conditionally included so React never mounts
//      a screen that isn't the current view (no hidden renders).

// ── Constants ─────────────────────────────────────────────────────────────────

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "showStaleBanner": false,
  "density": "cozy",
  "collapseSidebar": false,
  "showHelpHints": false
}/*EDITMODE-END*/;

const LS_SIDEBAR_KEY = "keeper.sidebarCollapsed";

function findGreenhouseInData(greenhouseId) {
  if (!greenhouseId) return null;
  const greenhouseMap = (DATA && DATA.greenhouses) || {};
  const siteIds = Object.keys(greenhouseMap);
  for (let i = 0; i < siteIds.length; i += 1) {
    const siteId = siteIds[i];
    const rows = Array.isArray(greenhouseMap[siteId]) ? greenhouseMap[siteId] : [];
    const greenhouse = rows.find((item) => String(item.id) === String(greenhouseId));
    if (greenhouse) return { siteId, greenhouse };
  }
  return null;
}

function firstGreenhouseIdForSite(siteId) {
  if (siteId === undefined || siteId === null) return null;
  const rows = ((DATA && DATA.greenhouses) || {})[siteId];
  if (!Array.isArray(rows) || rows.length === 0) return null;
  return rows[0]?.id ?? null;
}

function normalizeMapRouteState(state) {
  if (!state || typeof state !== "object") return null;
  const hasGreenhouseKey = Object.prototype.hasOwnProperty.call(state, "greenhouseId")
    || Object.prototype.hasOwnProperty.call(state, "greenhouse_id");
  const hasZoneKey = Object.prototype.hasOwnProperty.call(state, "zoneId")
    || Object.prototype.hasOwnProperty.call(state, "zone_id");
  const hasHiveKey = Object.prototype.hasOwnProperty.call(state, "hiveId")
    || Object.prototype.hasOwnProperty.call(state, "hive_id");
  const hasDeviceKey = Object.prototype.hasOwnProperty.call(state, "deviceId")
    || Object.prototype.hasOwnProperty.call(state, "device_id");
  if (!hasGreenhouseKey && !hasZoneKey && !hasHiveKey && !hasDeviceKey) return null;
  return {
    hasGreenhouseKey,
    hasZoneKey,
    hasHiveKey,
    hasDeviceKey,
    greenhouseId: hasGreenhouseKey ? (state.greenhouseId ?? state.greenhouse_id ?? null) : null,
    zoneId: hasZoneKey ? (state.zoneId ?? state.zone_id ?? null) : null,
    hiveId: hasHiveKey ? (state.hiveId ?? state.hive_id ?? null) : null,
    deviceId: hasDeviceKey ? (state.deviceId ?? state.device_id ?? null) : null,
  };
}

// ── App root component ────────────────────────────────────────────────────────

function App() {
  // ── Auth & tenant state ──────────────────────────────────────────────────
  // authState shape: { status: "loading" | "authed" | "anon", user: object|null, tenant: object|null }
  // • "loading" — session check in-flight; render a neutral loading screen.
  // • "authed"  — user is authenticated; render the full shell.
  // • "anon"    — no active session; render the login screen.
  const [authState, setAuthState] = React.useState({
    status: "loading",
    user: null,
    tenant: null,
    restoreMessage: "",
  });

  // ── Router state ─────────────────────────────────────────────────────────
  // Initialise from router.current() so the correct screen is shown
  // immediately without waiting for a hashchange event.
  const [route, setRoute] = React.useState(() => Keeper.router.current());
  const [routeState, setRouteState] = React.useState(() => {
    return Keeper.router.currentState ? Keeper.router.currentState() : null;
  });
  const [selectedSiteId, setSelectedSiteId] = React.useState(() => {
    return DATA?.sites?.[0]?.id ?? null;
  });
  const [selectedGreenhouseId, setSelectedGreenhouseId] = React.useState(null);
  const [selectedZoneId, setSelectedZoneId] = React.useState(null);
  const [selectedDeviceId, setSelectedDeviceId] = React.useState(null);

  // ── Hive overlay / full-page state ───────────────────────────────────────
  const [hive, setHive] = React.useState(null);          // selected hive id (detail panel)
  const [showHive, setShowHive] = React.useState(false); // full-page hive view toggle

  // ── Design tweaks & sidebar collapse ────────────────────────────────────
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [sidebarCollapsed, setSidebarCollapsed] = React.useState(
    () => localStorage.getItem(LS_SIDEBAR_KEY) === "true"
  );

  // ── Sidebar collapse helpers ─────────────────────────────────────────────
  function toggleSidebar() {
    setSidebarCollapsed(prev => {
      const next = !prev;
      localStorage.setItem(LS_SIDEBAR_KEY, String(next));
      return next;
    });
  }

  // ── Session revalidation on mount ───────────────────────────────────────
  // When a keeper.token exists in localStorage, attempt to silently resume
  // the session by calling revalidateSession().  On success the session data
  // lands in window.DATA (via revalidateSession's own merge) so we read
  // activeTenant from there.  On any failure (401, network error, etc.) we
  // wipe the stale token and drop the user to the login screen.
  // If there is no token at all we skip the round-trip and go straight to
  // "anon" so the user sees the login form with zero flicker.
  // [TASK_START:T012]
  React.useEffect(() => {
    const token = localStorage.getItem("keeper.token");
    if (!token) {
      setAuthState({ status: "anon", user: null, tenant: null, restoreMessage: "" });
      return;
    }

    Keeper.revalidateSession().then(ok => {
      if (ok) {
        const user = (window.DATA && window.DATA.user) || null;
        const tenant = (window.DATA && window.DATA.activeTenant) || null;
        setAuthState({ status: "authed", user, tenant, restoreMessage: "" });
      } else {
        // Session invalid or backend unreachable — clear stale credentials
        // and send the user back to the login screen.
        localStorage.removeItem("keeper.token");
        localStorage.removeItem("keeper.activeTenant");
        setAuthState({
          status: "anon",
          user: null,
          tenant: null,
          restoreMessage: "Не удалось восстановить сессию. Войдите заново.",
        });
      }
    });
  }, []); // run once on mount

  async function handleLogout() {
    // [TASK_START:T009]
    await Keeper.api.logout().catch(() => null);
    localStorage.removeItem("keeper.token");
    localStorage.removeItem("keeper.activeTenant");
    window.DATA = Object.assign({}, window.DATA || {}, { user: null, activeTenant: null });
    setAuthState({ status: "anon", user: null, tenant: null, restoreMessage: "" });
    Keeper.router.go("overview");
    // [TASK_COMPLETE:T009]
  }

  function mapContextRouteState() {
    const resolvedGreenhouseId = selectedGreenhouseId ?? firstGreenhouseIdForSite(selectedSiteId) ?? null;
    return {
      greenhouseId: resolvedGreenhouseId,
      zoneId: selectedZoneId ?? null,
      deviceId: selectedDeviceId ?? null,
    };
  }

  function navigate(routeId, state) {
    if (state === undefined) {
      Keeper.router.go(routeId);
      return;
    }
    Keeper.router.go(routeId, state);
  }

  function navigateFromScreen(routeId, state) {
    if (state !== undefined) {
      navigate(routeId, state);
      return;
    }
    if (routeId === "hives") {
      navigate("hives", mapContextRouteState());
      return;
    }
    navigate(routeId);
  }

  // ── Route change listener ────────────────────────────────────────────────
  // Sync React route state with the hash router's keeper:route custom event.
  // Also clears the hive overlay whenever the user navigates to a new screen.
  //
  // On mount we also re-read router.current() as a belt-and-suspenders guard:
  // router.init() runs synchronously before React mounts and updates _current
  // from window.location.hash, so this call ensures the component state always
  // reflects the correct initial route even if the useState lazy initialiser
  // captured a stale value for any reason (e.g. race with Babel transpilation).
  React.useEffect(() => {
    // Sync once on mount so a direct URL load (e.g. /#alerts) always shows
    // the correct screen on the very first render.
    setRoute(Keeper.router.current());
    setRouteState(Keeper.router.currentState ? Keeper.router.currentState() : null);

    function onRouteChange(e) {
      setRoute(e.detail.route);
      setRouteState(e.detail.state ?? null);
      setHive(null);
      setShowHive(false);
    }
    window.addEventListener("keeper:route", onRouteChange);
    return () => window.removeEventListener("keeper:route", onRouteChange);
  }, []);

  // [TASK_START:T004]
  React.useEffect(() => {
    if (!["hives", "zones", "hiveslist", "hivepage", "devices"].includes(route)) return;
    const context = normalizeMapRouteState(routeState);
    if (!context) return;
    if (context.hasGreenhouseKey) {
      setSelectedGreenhouseId(context.greenhouseId);
      const greenhouseRef = findGreenhouseInData(context.greenhouseId);
      if (greenhouseRef) {
        const parsedSiteId = Number(greenhouseRef.siteId);
        setSelectedSiteId(Number.isNaN(parsedSiteId) ? greenhouseRef.siteId : parsedSiteId);
      }
    }
    if (context.hasZoneKey) setSelectedZoneId(context.zoneId);
    if (context.hasHiveKey) setHive(context.hiveId);
    if (context.hasDeviceKey) setSelectedDeviceId(context.deviceId);
  }, [route, routeState]);
  // [TASK_COMPLETE:T004]

  React.useEffect(() => {
    const selectedGreenhouseRef = findGreenhouseInData(selectedGreenhouseId);
    const resolvedSiteId = selectedGreenhouseRef
      ? (Number.isNaN(Number(selectedGreenhouseRef.siteId)) ? selectedGreenhouseRef.siteId : Number(selectedGreenhouseRef.siteId))
      : selectedSiteId;
    const site = (DATA?.sites || []).find((item) => item.id === resolvedSiteId);
    const greenhouse = selectedGreenhouseRef?.greenhouse || null;
    const zone = (selectedGreenhouseId && DATA?.zones?.[selectedGreenhouseId] || []).find(
      (item) => item.id === selectedZoneId
    );

    Keeper.breadcrumbs.setContext({
      tenantName: authState?.tenant?.name || "Ahal Agro Park",
      siteName: site?.name || "Теплица",
      greenhouseName: greenhouse?.name || "Теплица",
      zoneName: zone?.name || zone?.id || "Зона",
    });
  }, [authState?.tenant?.name, selectedSiteId, selectedGreenhouseId, selectedZoneId]);
  // [TASK_COMPLETE:T012]

  // ── Derived values ───────────────────────────────────────────────────────
  const densCls = tweaks.density === "cozy"
    ? "dens-cozy"
    : tweaks.density === "comfy"
    ? "dens-comfy"
    : "";

  // sidebar collapsed: persisted user preference OR tweaks-panel override
  const isCollapsed = sidebarCollapsed || tweaks.collapseSidebar;

  // ── Login guard ──────────────────────────────────────────────────────────
  // Show a loading indicator while the session is being resolved.
  // Show the login screen when the user is anonymous.
  // The TweaksPanel provides a quick "skip to app" shortcut for demos.
  if (authState.status === "loading") {
    // Full-screen splash rendered while the session revalidation round-trip is
    // in-flight.  Keeps the user on a neutral dark screen so they never see a
    // flash of the login form when they already have a valid token.
    return (
      <div className="app-loading" role="status" aria-label="Загрузка…" aria-busy="true">
        <img
          className="app-loading__logo"
          src="ds/assets/keeper-logo-mono-white.png"
          alt="Keeper"
        />
        <div className="app-loading__spinner" aria-hidden="true"/>
        <span className="app-loading__label">Загрузка…</span>
      </div>
    );
  }

  if (authState.status === "anon") {
    return (
      <>
        <Login
          sessionMessage={authState.restoreMessage}
          onLogin={({ user, tenant }) => setAuthState({ status: "authed", user, tenant, restoreMessage: "" })}
        />
        <TweaksPanel title="Tweaks">
          <TweakSection title="Демо">
            <TweakButton
              label="Перейти в приложение"
              onClick={() => setAuthState({ status: "authed", user: DATA.user || null, tenant: DATA.tenants[0], restoreMessage: "" })}
            />
          </TweakSection>
        </TweaksPanel>
      </>
    );
  }

  // ── Authenticated shell layout ───────────────────────────────────────────
  // Destructure for convenience — authState.status === "authed" at this point.
  const { user, tenant } = authState;

  return (
    <div
      className={"app " + densCls}
      style={isCollapsed ? { "--sidebar-current-w": "var(--sidebar-w-collapsed)" } : {}}
    >
      {/* Sidebar — wrapped so .nav-collapsed class can be applied without
          adding an extra DOM element to the CSS Grid layout */}
      <div className={isCollapsed ? "nav-collapsed" : ""} style={{ display: "contents" }}>
        <Sidebar
          route={route}
          // [TASK_START:T015]
          role={(user && user.role) || (tenant && tenant.role) || ""}
          user={user}
          tenant={tenant}
          // [TASK_COMPLETE:T015]
          onNavigate={r => navigateFromScreen(r)}
          onLogout={handleLogout}
          alertCount={DATA.alerts.filter(a => a.kind === "crit" || a.kind === "warn").length}
          taskCount={DATA.tasks.filter(t => t.col !== "done").length}
          collapsed={isCollapsed}
          onToggleCollapse={toggleSidebar}
        />
      </div>

      {/* Main content area: top bar + optional stale-data banner + screen */}
      <div className="main">
        <TopBar route={route}/>

        {tweaks.showStaleBanner && (
          <div className="banner">
            <i className="ri-wifi-off-line"></i>
            Связь с gateway GW-01 потеряна 2 мин назад · показаны последние известные значения
          </div>
        )}

        {/* ── Lazy screen container ──────────────────────────────────────
            Each screen is rendered only when its route is active.
            React unmounts inactive screens, keeping the DOM lean.
            Full-page HivePage is a special case: it overlays the entire
            main area regardless of route when showHive is true.          */}

        {showHive && (
          <HivePage hiveId={hive} onClose={() => { setShowHive(false); navigate("hives", mapContextRouteState()); }}/>
        )}

        {!showHive && route === "overview"         && (
          <Overview
            tenant={tenant}
            onNav={r => navigateFromScreen(r)}
            onOpenHive={(hiveId) => {
              setHive(hiveId);
              navigate("hivepage");
            }}
          />
        )}
        {!showHive && route === "sites"            && (
          <Sites
            onNav={r => navigateFromScreen(r)}
            onSelectSite={(siteId) => {
              setSelectedSiteId(siteId);
              setSelectedGreenhouseId(null);
              setSelectedZoneId(null);
              setSelectedDeviceId(null);
            }}
          />
        )}
        {!showHive && route === "site"             && (
          <Site
            siteId={selectedSiteId}
            onNav={r => navigateFromScreen(r)}
            onOpenGreenhouses={(siteId) => {
              if (siteId !== undefined && siteId !== null) setSelectedSiteId(siteId);
              setSelectedGreenhouseId(null);
              setSelectedZoneId(null);
              setSelectedDeviceId(null);
              navigate("greenhouses");
            }}
          />
        )}
        {/* [TASK_START:T007] */}
        {!showHive && route === "greenhouses"      && (
          <GreenhousesList
            siteId={selectedSiteId}
            selectedGreenhouseId={selectedGreenhouseId}
            onNav={r => navigateFromScreen(r)}
            onSelectGreenhouse={(greenhouseId) => {
              setSelectedGreenhouseId(greenhouseId);
              setSelectedZoneId(null);
              setSelectedDeviceId(null);
            }}
          />
        )}
        {!showHive && route === "zones"            && (
          <ZonesList
            greenhouseId={selectedGreenhouseId}
            onNav={r => navigateFromScreen(r)}
            onSelectZone={(zoneId) => {
              setSelectedZoneId(zoneId);
              setSelectedDeviceId(null);
            }}
          />
        )}
        {!showHive && route === "hiveslist"        && (
          <HivesList
            greenhouseId={selectedGreenhouseId}
            zoneId={selectedZoneId}
            onNav={r => navigateFromScreen(r)}
            onSelectHive={(hiveId) => {
              setHive(hiveId);
            }}
          />
        )}
        {/* [TASK_COMPLETE:T007] */}
        {!showHive && route === "hives"            && (
          <GreenhouseMap
            selectedHive={hive}
            selectedGreenhouseId={selectedGreenhouseId}
            selectedZoneId={selectedZoneId}
            onSelectHive={(h) => { setHive(h); }}
            onDrillZone={(payload) => {
              setSelectedGreenhouseId(payload?.greenhouseId ?? selectedGreenhouseId);
              setSelectedZoneId(payload?.zoneId ?? null);
              setSelectedDeviceId(null);
              navigate("hiveslist", {
                greenhouseId: payload?.greenhouseId ?? selectedGreenhouseId ?? null,
                zoneId: payload?.zoneId ?? null,
              });
            }}
            onDrillHive={(payload) => {
              setSelectedGreenhouseId(payload?.greenhouseId ?? selectedGreenhouseId);
              setSelectedZoneId(payload?.zoneId ?? null);
              setSelectedDeviceId(null);
              setHive(payload?.hiveId ?? null);
              navigate("hivepage", {
                greenhouseId: payload?.greenhouseId ?? selectedGreenhouseId ?? null,
                zoneId: payload?.zoneId ?? null,
                hiveId: payload?.hiveId ?? null,
              });
            }}
            onDrillDevice={(payload) => {
              setSelectedGreenhouseId(payload?.greenhouseId ?? selectedGreenhouseId);
              setSelectedZoneId(payload?.zoneId ?? null);
              setSelectedDeviceId(payload?.deviceId ?? null);
              navigate("devices", {
                greenhouseId: payload?.greenhouseId ?? selectedGreenhouseId ?? null,
                zoneId: payload?.zoneId ?? null,
                deviceId: payload?.deviceId ?? null,
              });
            }}
          />
        )}
        {!showHive && route === "devices"          && (
          <Devices
            routeState={routeState}
            onNav={navigateFromScreen}
          />
        )}
        {!showHive && route === "telemetry"        && <Telemetry/>}
        {!showHive && route === "microclimate"     && <Microclimate/>}
        {!showHive && route === "alerts"           && <Alerts/>}
        {!showHive && route === "recommendations"  && <Recommendations onNav={r => navigateFromScreen(r)}/>}
        {!showHive && route === "tasks"            && <TasksKanban/>}
        {!showHive && route === "rules"            && <Rules/>}
        {!showHive && route === "commands"         && <Commands/>}
        {!showHive && route === "ota"              && <OTA/>}
        {!showHive && route === "reports"          && <Reports/>}
        {!showHive && route === "settings"         && <Settings/>}
        {!showHive && route === "roi"              && <RoiCalculator/>}
        {!showHive && route === "hivepage"         && <HivePage hiveId={hive} onClose={() => navigate("hives", mapContextRouteState())}/>}
        {!showHive && route === "simulator"        && <SimulatorStand/>}
      </div>

      {/* Hive detail slide-over panel + "open full page" button */}
      {hive && !showHive && (
        <>
          <HiveDetail hiveId={hive} onClose={() => setHive(null)}/>
          <button
            className="btn btn--secondary btn--sm"
            style={{
              position: "fixed",
              right: 440,
              bottom: 24,
              zIndex: 25,
              boxShadow: "var(--shadow-md)",
            }}
            onClick={() => { setShowHive(true); setHive(null); }}
          >
            <i className="ri-fullscreen-line"></i>Открыть полную страницу
          </button>
        </>
      )}

      {/* Design tweaks panel — fixed bottom-right, available in all authed views */}
      <TweaksPanel title="Tweaks">
        <TweakSection title="Состояние">
          <TweakToggle
            label="Баннер «связь потеряна»"
            value={tweaks.showStaleBanner}
            onChange={v => setTweak("showStaleBanner", v)}
          />
          <TweakToggle
            label="Свернуть сайдбар"
            value={tweaks.collapseSidebar}
            onChange={v => setTweak("collapseSidebar", v)}
          />
        </TweakSection>
        <TweakSection title="Плотность">
          <TweakRadio
            value={tweaks.density}
            onChange={v => setTweak("density", v)}
            options={[
              { value: "cozy",    label: "Cozy"    },
              { value: "default", label: "Default" },
              { value: "comfy",   label: "Comfy"   },
            ]}
          />
        </TweakSection>
        <TweakSection title="Навигация">
          <TweakButton label="К дашборду"                onClick={() => Keeper.router.go("overview")}/>
          <TweakButton label="Карта теплицы"             onClick={() => navigate("hives", mapContextRouteState())}/>
          <TweakButton label="Полная страница улья"      onClick={() => { setShowHive(true); setHive(null); }}/>
          <TweakButton label="Команды + подтверждение"   onClick={() => Keeper.router.go("commands")}/>
          <TweakButton label="Канбан задач"              onClick={() => Keeper.router.go("tasks")}/>
          <TweakButton label="OTA"                       onClick={() => Keeper.router.go("ota")}/>
          <TweakButton label="ROI-калькулятор"           onClick={() => Keeper.router.go("roi")}/>
          <TweakButton label="Виртуальный стенд"           onClick={() => Keeper.router.go("simulator")}/>
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

// ── Router bootstrap ───────────────────────────────────────────────────────────
// Register every valid route, set the default fallback, then initialise the
// hash listener.  Must run before ReactDOM.createRoot so that router.current()
// returns the correct value when App's useState initialiser fires.
Keeper.router
  .registerAll([
    "overview",
    "sites",
    "site",
    "hives",
    "greenhouses",
    "zones",
    "hiveslist",
    "devices",
    "telemetry",
    "microclimate",
    "alerts",
    "recommendations",
    "tasks",
    "rules",
    "commands",
    "ota",
    "reports",
    "settings",
    "roi",
    "hivepage",
    "simulator",
  ])
  .setDefault("overview")
  .init();

// ── Mount ──────────────────────────────────────────────────────────────────────
// Hydrate demo data (no-op if backend is unavailable) then render the React tree.
window.Keeper.hydrate().finally(() => {
  ReactDOM.createRoot(document.getElementById("root")).render(<App/>);
});
