import { lazy, Suspense, useEffect, useRef, useState } from "react";

import { getAudioContentCreateCharacterIdFromPath, getAudioContentDetailRouteFromPath, getAudioContentEditRouteFromPath, getAudioContentListCharacterIdFromPath, getCharacterEditIdFromPath, getCharacterIdFromPath, getCommunityPostCreateCharacterIdFromPath, getCommunityPostListCharacterIdFromPath, getFanTalkListCharacterIdFromPath, getSeriesCreateCharacterIdFromPath, getSeriesDetailRouteFromPath, getSeriesEditRouteFromPath, getSeriesListCharacterIdFromPath, getSeriesOrderCharacterIdFromPath, navigateTo, useBrowserLocation } from "@/app/browser-location";
import { routePaths } from "@/app/route-paths";
import { useAuthSession } from "@/features/auth/model/auth-session-context";
import { authSessionStorage } from "@/features/auth/model/auth-session-storage";
import type { ApiClient } from "@/shared/api/client";
import type { ApiMode } from "@/shared/config/env";
import { MockModeBanner } from "@/shared/ui/mock-mode-banner";
import { PageState } from "@/shared/ui/page-state";

const AudioContentDetailPage = lazy(() => import("@/features/audio-contents/pages/AudioContentDetailPage").then(({ AudioContentDetailPage }) => ({ default: AudioContentDetailPage })));
const AudioContentFormPage = lazy(() => import("@/features/audio-contents/pages/AudioContentFormPage").then(({ AudioContentFormPage }) => ({ default: AudioContentFormPage })));
const AudioContentListPage = lazy(() => import("@/features/audio-contents/pages/AudioContentListPage").then(({ AudioContentListPage }) => ({ default: AudioContentListPage })));
const CharacterCreatePage = lazy(() => import("@/features/characters/pages/CharacterCreatePage").then(({ CharacterCreatePage }) => ({ default: CharacterCreatePage })));
const CharacterDetailPage = lazy(() => import("@/features/characters/pages/CharacterDetailPage").then(({ CharacterDetailPage }) => ({ default: CharacterDetailPage })));
const CharacterEditPage = lazy(() => import("@/features/characters/pages/CharacterEditPage").then(({ CharacterEditPage }) => ({ default: CharacterEditPage })));
const CharacterListPage = lazy(() => import("@/features/characters/pages/CharacterListPage").then(({ CharacterListPage }) => ({ default: CharacterListPage })));
const CommunityPostFormPage = lazy(() => import("@/features/community-posts/pages/CommunityPostFormPage").then(({ CommunityPostFormPage }) => ({ default: CommunityPostFormPage })));
const CommunityPostListPage = lazy(() => import("@/features/community-posts/pages/CommunityPostListPage").then(({ CommunityPostListPage }) => ({ default: CommunityPostListPage })));
const FanTalkListPage = lazy(() => import("@/features/fan-talks/pages/FanTalkListPage").then(({ FanTalkListPage }) => ({ default: FanTalkListPage })));
const SeriesDetailPage = lazy(() => import("@/features/series/pages/SeriesDetailPage").then(({ SeriesDetailPage }) => ({ default: SeriesDetailPage })));
const SeriesFormPage = lazy(() => import("@/features/series/pages/SeriesFormPage").then(({ SeriesFormPage }) => ({ default: SeriesFormPage })));
const SeriesListPage = lazy(() => import("@/features/series/pages/SeriesListPage").then(({ SeriesListPage }) => ({ default: SeriesListPage })));
const SeriesOrderPage = lazy(() => import("@/features/series/pages/SeriesOrderPage").then(({ SeriesOrderPage }) => ({ default: SeriesOrderPage })));

const focusableSelector = "button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])";
const sessionExpiredNotice = "세션이 만료되었습니다. 다시 로그인하세요.";

function NavLink() {
  return (
    <a
      className="rounded-md px-3 py-2 text-sm font-semibold text-accent-foreground hover:bg-accent"
      href={routePaths.aiCharacters}
      onClick={(event) => {
        event.preventDefault();
        navigateTo(routePaths.aiCharacters);
      }}
    >
      AI 캐릭터
    </a>
  );
}

export function ProtectedAdminShell({ apiClient, apiMode, routeError }: { readonly apiClient: ApiClient; readonly apiMode: ApiMode; readonly routeError: string | null }) {
  const auth = useAuthSession();
  const uploadAuth = {
    clearSession: () => auth.clearSession(sessionExpiredNotice),
    getToken: () => authSessionStorage.read()?.token ?? null,
    onAuthExpired: () => navigateTo(routePaths.login),
  };
  const location = useBrowserLocation();
  const audioContentDetailRoute = getAudioContentDetailRouteFromPath(location.path);
  const audioContentEditRoute = getAudioContentEditRouteFromPath(location.path);
  const audioContentCreateCharacterId = getAudioContentCreateCharacterIdFromPath(location.path);
  const audioContentListCharacterId = getAudioContentListCharacterIdFromPath(location.path);
  const seriesDetailRoute = getSeriesDetailRouteFromPath(location.path);
  const seriesEditRoute = getSeriesEditRouteFromPath(location.path);
  const seriesCreateCharacterId = getSeriesCreateCharacterIdFromPath(location.path);
  const seriesOrderCharacterId = getSeriesOrderCharacterIdFromPath(location.path);
  const seriesListCharacterId = getSeriesListCharacterIdFromPath(location.path);
  const communityPostListCharacterId = getCommunityPostListCharacterIdFromPath(location.path);
  const communityPostCreateCharacterId = getCommunityPostCreateCharacterIdFromPath(location.path);
  const fanTalkListCharacterId = getFanTalkListCharacterIdFromPath(location.path);
  const characterEditId = getCharacterEditIdFromPath(location.path);
  const characterId = getCharacterIdFromPath(location.path);
  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
  const menuButtonRef = useRef<HTMLButtonElement>(null);
  const closeButtonRef = useRef<HTMLButtonElement>(null);
  const mobileMenuRef = useRef<HTMLElement>(null);
  const mainRef = useRef<HTMLElement>(null);
  const shouldRestoreMenuFocusRef = useRef(false);

  useEffect(() => {
    if (window.matchMedia === undefined) {
      return undefined;
    }

    const desktopMediaQuery = window.matchMedia("(min-width: 1024px)");

    function closeOnDesktopMatch() {
      if (!desktopMediaQuery.matches) {
        return;
      }

      shouldRestoreMenuFocusRef.current = false;
      setIsMobileMenuOpen(false);
    }

    closeOnDesktopMatch();
    desktopMediaQuery.addEventListener("change", closeOnDesktopMatch);

    return () => desktopMediaQuery.removeEventListener("change", closeOnDesktopMatch);
  }, []);

  useEffect(() => {
    if (isMobileMenuOpen || !shouldRestoreMenuFocusRef.current) {
      return;
    }

    shouldRestoreMenuFocusRef.current = false;
    menuButtonRef.current?.focus();
  }, [isMobileMenuOpen]);

  useEffect(() => {
    if (!isMobileMenuOpen) {
      return undefined;
    }

    closeButtonRef.current?.focus();

    function closeOnEscape(event: KeyboardEvent) {
      if (event.key === "Escape") {
        shouldRestoreMenuFocusRef.current = true;
        setIsMobileMenuOpen(false);
      }
    }

    window.addEventListener("keydown", closeOnEscape);

    return () => window.removeEventListener("keydown", closeOnEscape);
  }, [isMobileMenuOpen]);

  function keepFocusInMobileMenu(event: React.KeyboardEvent<HTMLElement>) {
    if (event.key !== "Tab") {
      return;
    }

    const focusableElements = Array.from(mobileMenuRef.current?.querySelectorAll<HTMLElement>(focusableSelector) ?? []);
    const firstElement = focusableElements[0];
    const lastElement = focusableElements.at(-1);

    if (firstElement === undefined || lastElement === undefined) {
      return;
    }

    if (event.shiftKey && document.activeElement === firstElement) {
      event.preventDefault();
      lastElement.focus();
      return;
    }

    if (!event.shiftKey && document.activeElement === lastElement) {
      event.preventDefault();
      firstElement.focus();
    }
  }

  function closeMobileMenu() {
    shouldRestoreMenuFocusRef.current = true;
    setIsMobileMenuOpen(false);
  }

  return (
    <div className="min-h-[100dvh] bg-background text-foreground">
      <div aria-hidden={isMobileMenuOpen} className="flex min-h-[100dvh] flex-col" inert={isMobileMenuOpen}>
        <MockModeBanner apiMode={apiMode} />
        <div className="flex min-h-0 flex-1">
          <a className="sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-modal focus:rounded-md focus:bg-card focus:px-4 focus:py-2 focus:text-link" href="#app-main" onClick={(event) => {
            event.preventDefault();
            mainRef.current?.focus();
          }}>
            본문으로 건너뛰기
          </a>
          <aside className="hidden w-60 shrink-0 border-r border-border bg-card p-4 lg:block">
            <p className="mb-4 text-xs font-semibold text-info">AI CHARACTER ADMIN</p>
            <nav aria-label="데스크톱 주 메뉴" className="flex flex-col gap-2">
              <NavLink />
            </nav>
          </aside>
          <div className="flex min-w-0 flex-1 flex-col">
            <header className="flex min-h-14 flex-wrap items-center justify-between gap-3 border-b border-border bg-card px-4 py-2" role="banner">
              <div className="flex min-w-0 items-center gap-3">
                <button
                  aria-expanded={isMobileMenuOpen}
                  className="shrink-0 whitespace-nowrap rounded-md border border-input bg-card px-3 py-2 text-sm font-semibold lg:hidden"
                  onClick={() => setIsMobileMenuOpen(true)}
                  ref={menuButtonRef}
                  type="button"
                >
                  모바일 메뉴 열기
                </button>
                <nav aria-label="브레드크럼" className="hidden shrink-0 whitespace-nowrap text-sm text-muted-foreground sm:block">
                  <ol className="flex items-center gap-2">
                    <li>홈</li>
                    <li aria-hidden="true">/</li>
                    <li className="font-semibold text-foreground">AI 캐릭터</li>
                  </ol>
                </nav>
              </div>
              <button className="shrink-0 whitespace-nowrap rounded-md border border-input bg-primary px-4 py-2 font-semibold text-primary-foreground hover:bg-[var(--button-bg-hover)] active:bg-[var(--button-bg-active)]" onClick={() => void auth.logout()} type="button">
                로그아웃
              </button>
            </header>
            <main aria-label="AI 캐릭터 관리" className="min-h-0 flex-1 overflow-auto p-4" id="app-main" ref={mainRef} tabIndex={-1}>
              {location.successNotification === null ? null : (
                <p aria-label="작업 성공" className="mb-4 rounded-lg border border-border bg-success-surface p-3 text-sm font-semibold text-success" role="status">
                  {location.successNotification}
                </p>
              )}
              <Suspense fallback={<PageState state="loading" title="화면을 불러오는 중" />} key={location.path}>
                {location.path === routePaths.aiCharacterCreate ? <CharacterCreatePage apiClient={apiClient} /> : null}
                {characterEditId !== null ? <CharacterEditPage apiClient={apiClient} characterId={characterEditId} /> : null}
                {location.path !== routePaths.aiCharacterCreate && characterEditId === null && characterId === null && audioContentListCharacterId === null && audioContentCreateCharacterId === null && audioContentEditRoute === null && audioContentDetailRoute === null && communityPostCreateCharacterId === null && communityPostListCharacterId === null && fanTalkListCharacterId === null && seriesCreateCharacterId === null && seriesEditRoute === null && seriesListCharacterId === null && seriesOrderCharacterId === null && seriesDetailRoute === null ? <CharacterListPage apiClient={apiClient} routeError={routeError} /> : null}
                {location.path !== routePaths.aiCharacterCreate && characterEditId === null && characterId !== null ? <CharacterDetailPage apiClient={apiClient} characterId={characterId} /> : null}
                {audioContentListCharacterId !== null ? <AudioContentListPage apiClient={apiClient} characterId={audioContentListCharacterId} /> : null}
                {audioContentCreateCharacterId !== null ? <AudioContentFormPage apiClient={apiClient} characterId={audioContentCreateCharacterId} uploadAuth={uploadAuth} /> : null}
                {audioContentEditRoute !== null ? <AudioContentFormPage apiClient={apiClient} characterId={audioContentEditRoute.characterId} contentId={audioContentEditRoute.contentId} uploadAuth={uploadAuth} /> : null}
                {audioContentDetailRoute !== null ? <AudioContentDetailPage apiClient={apiClient} characterId={audioContentDetailRoute.characterId} contentId={audioContentDetailRoute.contentId} /> : null}
                {communityPostListCharacterId !== null ? <CommunityPostListPage apiClient={apiClient} characterId={communityPostListCharacterId} /> : null}
                {communityPostCreateCharacterId !== null ? <CommunityPostFormPage apiClient={apiClient} characterId={communityPostCreateCharacterId} /> : null}
                {fanTalkListCharacterId !== null ? <FanTalkListPage apiClient={apiClient} characterId={fanTalkListCharacterId} /> : null}
                {seriesListCharacterId !== null ? <SeriesListPage apiClient={apiClient} characterId={seriesListCharacterId} /> : null}
                {seriesCreateCharacterId !== null ? <SeriesFormPage apiClient={apiClient} characterId={seriesCreateCharacterId} /> : null}
                {seriesEditRoute !== null ? <SeriesFormPage apiClient={apiClient} characterId={seriesEditRoute.characterId} seriesId={seriesEditRoute.seriesId} /> : null}
                {seriesOrderCharacterId !== null ? <SeriesOrderPage apiClient={apiClient} characterId={seriesOrderCharacterId} /> : null}
                {seriesDetailRoute !== null ? <SeriesDetailPage apiClient={apiClient} characterId={seriesDetailRoute.characterId} seriesId={seriesDetailRoute.seriesId} /> : null}
              </Suspense>
            </main>
          </div>
        </div>
      </div>
      {isMobileMenuOpen ? (
        <div className="fixed inset-0 z-overlay bg-background/80 lg:hidden">
          <nav
            aria-label="모바일 주 메뉴"
            className="flex min-h-[100dvh] w-[min(15rem,50vw)] flex-col gap-3 border-r border-border bg-card p-4"
            onKeyDown={keepFocusInMobileMenu}
            ref={mobileMenuRef}
          >
            <button className="rounded-md border border-input bg-card px-3 py-2 text-sm font-semibold" onClick={closeMobileMenu} ref={closeButtonRef} type="button">
              모바일 메뉴 닫기
            </button>
            <NavLink />
          </nav>
        </div>
      ) : null}
    </div>
  );
}
