import React, { ReactNode, useEffect, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { Provider as JotaiProvider, useAtomValue } from 'jotai';
import {
  Box,
  Button,
  config,
  OverlayContainerProvider,
  PopOutContainerProvider,
  Text,
  toRem,
  TooltipContainerProvider,
} from 'folds';
import { RouterProvider } from 'react-router';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

import { ClientConfigLoader } from '../components/ClientConfigLoader';
import { ClientConfigProvider } from '../hooks/useClientConfig';
import { ConfigConfigError, ConfigConfigLoading } from './ConfigConfig';
import { FeatureCheck } from './FeatureCheck';
import { createRouter } from './Router';
import { ScreenSizeProvider, useScreenSize } from '../hooks/useScreenSize';
import { useCompositionEndTracking } from '../hooks/useComposingCheck';
import { settingsAtom } from '../state/settings';
import { isWithinTimeWindow } from '../utils/timeWindow';
import { LotusToastContainer } from '../features/toast/LotusToastContainer';
import { useTauriNotificationBadge } from '../hooks/useTauriNotificationBadge';
import { useTauriWindowChrome } from '../hooks/useTauriWindowChrome';
import { isTauri } from '../hooks/useTauri';
import { TitleBar } from '../features/desktop/TitleBar';
import { customWindowChromeAtom } from '../state/customWindowChrome';
import { SeasonalEffect } from '../components/seasonal/SeasonalEffect';
import { applyCustomAccent, removeCustomAccent } from '../utils/accentColor';
import { zIndices } from '../styles/zIndex';
import { OIDC_CALLBACK_PATH } from './paths';
import { OidcCallback } from './auth/oidc/OidcCallback';

const FONT_MAP: Record<string, string> = {
  system: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
  inter: "'InterVariable', sans-serif",
  'jetbrains-mono': "'JetBrains Mono', monospace",
  'fira-code': "'Fira Code', monospace",
};

function AppearanceEffects() {
  const settings = useAtomValue(settingsAtom);

  useEffect(() => {
    const color = settings.mentionHighlightColor;
    if (color) {
      document.body.style.setProperty('--mention-highlight-bg', color);
      // WCAG 2.1 relative luminance with gamma linearization
      const toLinear = (c: number) => {
        const s = c / 255;
        return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
      };
      const r = parseInt(color.slice(1, 3), 16);
      const g = parseInt(color.slice(3, 5), 16);
      const b = parseInt(color.slice(5, 7), 16);
      const lum = 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
      document.body.style.setProperty('--mention-highlight-text', lum > 0.179 ? '#000' : '#fff');
      // Derive a visible border: same hue, reduced alpha
      document.body.style.setProperty('--mention-highlight-border', `rgba(${r},${g},${b},0.5)`);
    } else {
      document.body.style.removeProperty('--mention-highlight-bg');
      document.body.style.removeProperty('--mention-highlight-text');
      document.body.style.removeProperty('--mention-highlight-border');
    }
  }, [settings.mentionHighlightColor]);

  useEffect(() => {
    // Custom accent color applies only to non-TDS themes. When Lotus Terminal
    // (TDS) is active it has its own fixed palette, so we remove any overrides.
    const accent = settings.customAccentColor;
    if (accent && !settings.lotusTerminal && applyCustomAccent(accent)) {
      return () => removeCustomAccent();
    }
    removeCustomAccent();
    return undefined;
  }, [settings.customAccentColor, settings.lotusTerminal]);

  useEffect(() => {
    const font = FONT_MAP[settings.fontFamily ?? 'inter'] ?? FONT_MAP.inter;
    document.body.style.setProperty('--font-secondary', font);
  }, [settings.fontFamily]);

  return null;
}

function TauriEffects() {
  useTauriNotificationBadge();
  return null;
}

// P5-47 — opt-in TDS window chrome. `useTauriWindowChrome` keeps the native OS
// window decorations in sync with the setting; when a desktop user enables
// custom chrome we replace the OS titlebar with <TitleBar/>. When off (the
// default, and always in the browser) this returns children unchanged, so there
// is zero layout impact for everyone else.
function DesktopChrome({ children }: { children: ReactNode }) {
  const customChrome = useAtomValue(customWindowChromeAtom);
  useTauriWindowChrome();
  const useChrome = isTauri() && customChrome;
  // Keep the wrapper element structure STABLE across the toggle so flipping the
  // setting never changes the element type in `children`'s ancestry — otherwise
  // React would unmount/remount the whole RouterProvider subtree (losing scroll,
  // menus, unsaved composer state). When off, both wrappers use `display:contents`
  // so they generate no box → zero layout impact (also the browser default path).
  return (
    <div
      style={
        useChrome
          ? // Match html/#root (100dvh), NOT 100vh — in the Tauri webview 100vh
            // can exceed the visible height after decorations are stripped, which
            // makes the timeline's scroll container taller than the viewport and
            // sends the virtual paginator into a runaway back-pagination loop.
            { display: 'flex', flexDirection: 'column', height: '100dvh' }
          : { display: 'contents' }
      }
    >
      {useChrome && <TitleBar />}
      <div style={useChrome ? { flexGrow: 1, minHeight: 0 } : { display: 'contents' }}>
        {children}
      </div>
    </div>
  );
}

function NightLightOverlay() {
  const settings = useAtomValue(settingsAtom);
  const scheduled = settings.nightLightEnabled && settings.nightLightSchedule;
  // When scheduled, re-evaluate the active window every minute so the overlay
  // turns on/off automatically at the start/end times without a reload.
  const [, forceTick] = useState(0);
  useEffect(() => {
    if (!scheduled) return undefined;
    const id = setInterval(() => forceTick((n) => n + 1), 60_000);
    return () => clearInterval(id);
  }, [scheduled]);

  if (!settings.nightLightEnabled) return null;
  if (
    settings.nightLightSchedule &&
    !isWithinTimeWindow(settings.nightLightStart, settings.nightLightEnd)
  ) {
    return null;
  }
  return (
    <div
      aria-hidden="true"
      style={{
        position: 'fixed',
        inset: 0,
        pointerEvents: 'none',
        zIndex: zIndices.nightLight,
        backgroundColor: `rgba(255, 140, 0, ${(settings.nightLightOpacity ?? 30) / 100})`,
      }}
    />
  );
}

const queryClient = new QueryClient();

function App() {
  const screenSize = useScreenSize();
  useCompositionEndTracking();

  const portalContainer = document.getElementById('portalContainer') ?? undefined;

  // OIDC/next-gen-auth callback is a real (non-hash) path: OAuth redirect_uris
  // can't contain a fragment, so it must be handled OUTSIDE the router. Render
  // the standalone callback page before the RouterProvider mounts. It needs no
  // app providers (it only touches the SDK + localStorage).
  if (window.location.pathname.endsWith(OIDC_CALLBACK_PATH)) {
    return <OidcCallback />;
  }

  return (
    <ErrorBoundary
      fallbackRender={({ error, resetErrorBoundary }) => (
        <Box
          direction="Column"
          alignItems="Center"
          justifyContent="Center"
          gap="400"
          style={{ height: '100vh', padding: config.space.S700, textAlign: 'center' }}
        >
          <Text size="H2">Something went wrong</Text>
          <Text size="T300" priority="300" style={{ maxWidth: toRem(400) }}>
            {error instanceof Error ? error.message : 'An unexpected error occurred.'}
          </Text>
          <Button variant="Primary" onClick={resetErrorBoundary}>
            <Text as="span" size="B400">
              Try again
            </Text>
          </Button>
        </Box>
      )}
    >
      <TooltipContainerProvider value={portalContainer}>
        <PopOutContainerProvider value={portalContainer}>
          <OverlayContainerProvider value={portalContainer}>
            <ScreenSizeProvider value={screenSize}>
              <FeatureCheck>
                <ClientConfigLoader
                  fallback={() => <ConfigConfigLoading />}
                  error={(err, retry, ignore) => (
                    <ConfigConfigError error={err} retry={retry} ignore={ignore} />
                  )}
                >
                  {(clientConfig) => (
                    <ClientConfigProvider value={clientConfig}>
                      <QueryClientProvider client={queryClient}>
                        <JotaiProvider>
                          <AppearanceEffects />
                          <TauriEffects />
                          <DesktopChrome>
                            <RouterProvider router={createRouter(clientConfig, screenSize)} />
                          </DesktopChrome>
                          <SeasonalEffect />
                          <NightLightOverlay />
                          <LotusToastContainer />
                        </JotaiProvider>
                        <ReactQueryDevtools initialIsOpen={false} />
                      </QueryClientProvider>
                    </ClientConfigProvider>
                  )}
                </ClientConfigLoader>
              </FeatureCheck>
            </ScreenSizeProvider>
          </OverlayContainerProvider>
        </PopOutContainerProvider>
      </TooltipContainerProvider>
    </ErrorBoundary>
  );
}

export default App;
