/**
 * Web client root — ticket #13, lot F2, extended by #90 (missing menus).
 * Mounted (instead of the desktop `App`) only when the HTTP transport is
 * selected (`VITE_TRANSPORT="http"`).
 *
 * Routes on the {@link WebSession} paired flag: not paired ⇒ {@link PairingScreen};
 * paired ⇒ the read-only {@link WebWorkspace}, or a Settings section opened from
 * the "Paramètres" menu (#90). It subscribes to the session's `onUnauthorized`
 * signal so a `401` from any `/api/invoke` (expired/missing cookie) drops back to
 * pairing automatically. "Se déconnecter" (F6) revokes the server session
 * (`POST /api/logout`), tears down the live WS singleton, and returns to pairing.
 *
 * "Paramètres" (#90) replaces the former single "Appareils" header toggle with a
 * menu of sections: **Profils IA** (mounts the transport-neutral
 * {@link ProfilesSettings}), **Appareils** (#77, mounts the shared
 * {@link DevicesScreen} — what makes a headless install usable: generating a
 * pairing code from an already-paired phone instead of restarting the server with
 * `--new-code`), and **Déploiement**, disabled — the embedded server is
 * desktop-only (a web client is *served by* it, so it must not reconfigure or
 * stop it, see `adapters/http/unsupported.ts`).
 */

import { useCallback, useEffect, useState, type ReactNode } from "react";

import { Button } from "@/shared";
import { disconnectWebLive, getWebSession, type WebSession } from "@/adapters/http";
import { DevicesScreen } from "@/features/devices";
import { ProfilesSettings } from "@/features/first-run";
import { PairingScreen } from "./PairingScreen";
import { WebWorkspace } from "./WebWorkspace";
import { WebMenuSheet } from "./WebMenuSheet";

interface WebAppProps {
  /** Injectable session (tests); defaults to the shared singleton. */
  session?: WebSession;
}

type WebSettingsSection = "aiProfiles" | "devices";

const SETTINGS_SECTION_LABEL: Record<WebSettingsSection, string> = {
  aiProfiles: "Profils IA",
  devices: "Appareils",
};

export function WebApp({ session }: WebAppProps = {}) {
  const webSession = session ?? getWebSession();
  const [paired, setPaired] = useState(() => webSession.isPaired());
  const [signingOut, setSigningOut] = useState(false);
  const [settingsSection, setSettingsSection] = useState<WebSettingsSection | null>(null);
  const [settingsMenuOpen, setSettingsMenuOpen] = useState(false);

  useEffect(() => {
    // A 401 anywhere clears the flag and fires this: return to pairing. The live
    // WS is torn down by the composition-root 401 handler (F6). This is also the
    // path for a *suffered* revocation (#77): another device revoked us, the
    // server rejects the next call, and we land back on pairing.
    return webSession.onUnauthorized(() => {
      setSettingsSection(null);
      setSettingsMenuOpen(false);
      setPaired(false);
    });
  }, [webSession]);

  async function signOut(): Promise<void> {
    if (signingOut) return;
    setSigningOut(true);
    try {
      // Revoke the server session (best-effort), then drop the live socket so it
      // stops reconnecting, and return to pairing regardless of the outcome.
      await webSession.logout();
    } finally {
      disconnectWebLive();
      setSigningOut(false);
      setSettingsSection(null);
      setSettingsMenuOpen(false);
      setPaired(false);
    }
  }

  /**
   * This device just revoked itself (#77) — a nominal action, not an edge case.
   * The cookie is already dead server-side; drop the local flag and the live
   * socket so nothing keeps retrying, and land on pairing immediately rather
   * than waiting for the next request to 401.
   */
  const onSessionEnded = useCallback(() => {
    webSession.forget();
    disconnectWebLive();
    setSettingsSection(null);
    setSettingsMenuOpen(false);
    setPaired(false);
  }, [webSession]);

  return (
    <div className="flex h-full flex-col bg-canvas text-content">
      <header
        data-testid="web-header"
        className="flex shrink-0 items-center justify-between border-b border-border px-4 py-3 pt-[max(0.75rem,env(safe-area-inset-top))] pl-[max(1rem,env(safe-area-inset-left))] pr-[max(1rem,env(safe-area-inset-right))] sm:px-6"
      >
        <div className="flex items-baseline gap-2">
          <h1 className="text-base font-semibold tracking-tight sm:text-lg">IdeA</h1>
          <span className="rounded-md bg-raised px-1.5 py-0.5 text-[0.65rem] font-medium uppercase text-muted">
            web
          </span>
        </div>
        {paired && (
          <div className="flex items-center gap-1">
            <Button
              variant="ghost"
              size="sm"
              aria-pressed={settingsMenuOpen}
              onClick={() => setSettingsMenuOpen(true)}
            >
              Paramètres
            </Button>
            <Button variant="ghost" size="sm" loading={signingOut} onClick={() => void signOut()}>
              Se déconnecter
            </Button>
          </div>
        )}
      </header>

      <div className="flex flex-1 flex-col overflow-hidden">
        {!paired ? (
          <PairingScreen session={webSession} onPaired={() => setPaired(true)} />
        ) : settingsSection === "aiProfiles" ? (
          <SettingsSectionScreen
            title={SETTINGS_SECTION_LABEL.aiProfiles}
            onBack={() => setSettingsSection(null)}
          >
            <ProfilesSettings />
          </SettingsSectionScreen>
        ) : settingsSection === "devices" ? (
          <SettingsSectionScreen
            title={SETTINGS_SECTION_LABEL.devices}
            onBack={() => setSettingsSection(null)}
          >
            <DevicesScreen onSessionEnded={onSessionEnded} />
          </SettingsSectionScreen>
        ) : (
          <WebWorkspace />
        )}
      </div>

      {settingsMenuOpen && (
        <WebMenuSheet
          title="Paramètres"
          onClose={() => setSettingsMenuOpen(false)}
          entries={[
            {
              id: "aiProfiles",
              label: SETTINGS_SECTION_LABEL.aiProfiles,
              active: settingsSection === "aiProfiles",
              onSelect: () => {
                setSettingsSection("aiProfiles");
                setSettingsMenuOpen(false);
              },
            },
            {
              id: "devices",
              label: SETTINGS_SECTION_LABEL.devices,
              active: settingsSection === "devices",
              onSelect: () => {
                setSettingsSection("devices");
                setSettingsMenuOpen(false);
              },
            },
            {
              id: "deployment",
              label: "Déploiement",
              active: false,
              disabled: true,
              secondaryLabel: "Desktop uniquement",
              hint: "Desktop uniquement",
              onSelect: () => {},
            },
          ]}
        />
      )}
    </div>
  );
}

/** A Settings section takes over the main area, with a "← Retour" affordance. */
function SettingsSectionScreen({
  title,
  onBack,
  children,
}: {
  title: string;
  onBack: () => void;
  children: ReactNode;
}) {
  return (
    <div className="flex h-full flex-col overflow-hidden">
      <div className="flex shrink-0 items-center gap-2 border-b border-border px-4 py-2 pl-[max(1rem,env(safe-area-inset-left))] pr-[max(1rem,env(safe-area-inset-right))]">
        <Button variant="ghost" size="sm" onClick={onBack}>
          ← Retour
        </Button>
        <span className="text-sm font-medium text-content">{title}</span>
      </div>
      <div className="flex-1 overflow-y-auto p-4 pb-[max(1rem,env(safe-area-inset-bottom))] pl-[max(1rem,env(safe-area-inset-left))] pr-[max(1rem,env(safe-area-inset-right))] sm:p-6">
        <div className="mx-auto w-full max-w-xl">{children}</div>
      </div>
    </div>
  );
}
