/**
 * @file App.tsx - Root application shell component
 */

import './App.css';
import './assets/fonts/Dancing_Script/DancingScript-Regular.ttf';
import { ToastContainer } from 'react-toastify';
import { useLocation, useMatches } from 'react-router-dom';
import { Navbar } from './components/Navbar.tsx';
import { ReauthProvider } from './contexts/ReauthContext.tsx';
import { Outlet } from 'react-router-dom';
import { useEffect, useRef, useState } from 'react';
import { useAuth } from './contexts/AuthContext.tsx';

/**
 * Root application shell component that conditionally renders the navigation bar (based on auth state and route config), a toast notification container, and the current route's content via React Router's Outlet.
 * Dynamically adjusts main content height based on navbar dimensions using a ResizeObserver.
 */
function App() {
  const location = useLocation();
  const matches = useMatches();
  const mainRef = useRef<HTMLDivElement>(null);
  const { username, token, sessionKey } = useAuth();

  // Extract route handle metadata from the deepest matched route with a handle defined
  const currentRoute = matches.filter((match) => match.handle).pop();
  // @ts-expect-error showNavbar exists
  const showNavbar = currentRoute?.handle?.showNavbar ?? true;
  // @ts-expect-error title exists
  const title = currentRoute?.handle?.title ?? '';

  // Only show navbar if user is authenticated and route allows navbar
  const shouldShowNavbar = showNavbar && username && token;

  // Automatically scroll to top of page on location change
  useEffect(() => {
    const root = document.querySelector('#root');
    if (root) {
      root.scrollIntoView({ behavior: 'instant' });
    }
  }, [location.pathname]);

  // Set page title
  useEffect(() => {
    document.title =
      (title ? `${title} | ` : '') + import.meta.env.VITE_APP_TITLE;
  }, [title]);

  const navbar = useRef<HTMLDivElement>(null);
  const [navbarHeight, setNavbarHeight] = useState<number | undefined>(
    undefined
  );
  const [pageHeight, setPageHeight] = useState('100vh');

  // Observe navbar height changes and compute the main content area height accordingly
  // Also sets the scroll-margin-top on #root so scroll-into-view doesn't hide content behind the navbar
  useEffect(() => {
    if (!navbar.current?.children[0]) {
      setNavbarHeight(0);
      setPageHeight(`calc(100vh)`);
      return;
    }

    const navbarElement = navbar.current.children[0] as HTMLElement;

    const resizeObserver = new ResizeObserver(() => {
      const height = navbarElement.getBoundingClientRect().height;
      setNavbarHeight(height);
      setPageHeight(`calc(100vh - ${height}px)`);

      const root = document.querySelector('#root');
      if (root) {
        (root as HTMLElement).style.setProperty(
          'scroll-margin-top',
          `${height}px`
        );
      }
    });

    resizeObserver.observe(navbarElement);

    return () => resizeObserver.disconnect();
  }, [shouldShowNavbar]);

  return (
    <div key={sessionKey}>
      <ToastContainer
        position={'bottom-right'}
        closeOnClick
        pauseOnFocusLoss
        draggable
        pauseOnHover
        theme={'colored'}
      />

      {shouldShowNavbar && (
        <div ref={navbar}>
          <Navbar />
        </div>
      )}

      <main
        ref={mainRef}
        className={'container-fluid d-flex flex-column p-3'}
        style={{
          minHeight: pageHeight,
          marginTop: shouldShowNavbar ? navbarHeight : '',
        }}
      >
        <ReauthProvider>
          <Outlet />
        </ReauthProvider>
      </main>
    </div>
  );
}

export default App;
