import { AppShell, Center, Loader, Stack, Text, useMantineColorScheme } from '@mantine/core';
import { useCallback, useEffect, useMemo, useState } from 'react';

import { BackendCrashModal } from '@/components/App/BackendCrashModal';
import { EjectConfirmModal } from '@/components/App/EjectConfirmModal';
import { ErrorBoundary } from '@/components/App/ErrorBoundary';
import { AppRouter } from '@/components/App/Router';
import { StatusBar } from '@/components/App/StatusBar';
import { TitleBar } from '@/components/App/TitleBar';
import { UpdateAvailableModal } from '@/components/App/UpdateAvailableModal';
import { NotepadContainer } from '@/components/Notepad/Container';
import { SERVER_READY_DELAY_MS, STATUSBAR_HEIGHT, TITLEBAR_HEIGHT } from '@/constants';
import { useUpdateChecker } from '@/hooks/useUpdateChecker';
import { useKoboldBackendsStore } from '@/stores/koboldBackends';
import { useLaunchConfigStore } from '@/stores/launchConfig';
import { usePreferencesStore } from '@/stores/preferences';
import type { InterfaceTab, Screen } from '@/types';
import type { DownloadItem } from '@/types/electron';
import type { KoboldCrashInfo } from '@/types/ipc';
import { getDefaultInterfaceTab } from '@/utils/interface';

export const App = () => {
  const [currentScreen, setCurrentScreen] = useState<Screen | null>(null);
  const [hasInitialized, setHasInitialized] = useState(false);
  const [activeInterfaceTab, setActiveInterfaceTab] = useState<InterfaceTab>('terminal');
  const [isServerReady, setIsServerReady] = useState(false);
  const [ejectConfirmModalOpen, setEjectConfirmModalOpen] = useState(false);
  const [crashInfo, setCrashInfo] = useState<KoboldCrashInfo | null>(null);
  const isInterfaceScreen = currentScreen === 'interface';

  const {
    resolvedColorScheme: appColorScheme,
    systemMonitoringEnabled,
    frontendPreference,
    imageGenerationFrontendPreference,
  } = usePreferencesStore();
  const { setColorScheme } = useMantineColorScheme();
  const { model, sdmodel, isTextMode, isImageGenerationMode } = useLaunchConfigStore();

  const defaultInterfaceTab = useMemo(
    () =>
      getDefaultInterfaceTab({
        frontendPreference,
        imageGenerationFrontendPreference,
        isImageGenerationMode,
        isTextMode,
      }),
    [frontendPreference, imageGenerationFrontendPreference, isTextMode, isImageGenerationMode],
  );

  useEffect(() => {
    const cleanup = window.electronAPI.kobold.onServerReady(() => {
      setTimeout(() => {
        setIsServerReady(true);
        setActiveInterfaceTab(defaultInterfaceTab);
      }, SERVER_READY_DELAY_MS);
    });

    return cleanup;
  }, [defaultInterfaceTab]);

  useEffect(() => {
    setColorScheme(appColorScheme);
  }, [appColorScheme, setColorScheme]);

  useEffect(() => {
    const updateTray = async () => {
      const config = await window.electronAPI.kobold.getSelectedConfig();
      const displayModel = model || sdmodel || null;
      const modelName = displayModel ? (displayModel.split('/').pop() ?? displayModel) : null;

      void window.electronAPI.app.updateTrayState({
        config: config ?? null,
        model: modelName,
        monitoringEnabled: systemMonitoringEnabled,
        screen: currentScreen,
      });
    };

    void updateTray();
  }, [currentScreen, model, sdmodel, systemMonitoringEnabled]);

  const performEject = useCallback(() => {
    window.electronAPI.kobold.stopKoboldCpp();
    setIsServerReady(false);
    setActiveInterfaceTab('terminal');
    setCurrentScreen('launch');
  }, []);

  useEffect(() => {
    const ejectCleanup = window.electronAPI.app.onTrayEject(() => {
      performEject();
    });

    return () => {
      ejectCleanup();
    };
  }, [performEject]);

  useEffect(() => {
    const crashCleanup = window.electronAPI.kobold.onKoboldCrashed((crashData) => {
      setCrashInfo(crashData);
    });

    return () => {
      crashCleanup();
    };
  }, []);

  const {
    updateInfo: binaryUpdateInfo,
    showUpdateModal,
    checkForUpdates,
    skipUpdate,
    closeModal,
  } = useUpdateChecker();

  const { handleDownload, loadingRemote } = useKoboldBackendsStore();

  const determineScreen = useCallback((currentVersion: unknown, hasSeenWelcome: boolean) => {
    if (!hasSeenWelcome) {
      setCurrentScreen('welcome');
    } else if (currentVersion) {
      setCurrentScreen('launch');
    } else {
      setCurrentScreen('download');
    }
  }, []);

  useEffect(() => {
    const checkInstallation = async () => {
      const [currentBackend, hasSeenWelcome] = await Promise.all([
        window.electronAPI.kobold.getCurrentBackend(),
        window.electronAPI.config.get('hasSeenWelcome') as Promise<boolean>,
      ]);

      determineScreen(currentBackend, hasSeenWelcome);
      setHasInitialized(true);
    };

    void checkInstallation();
  }, [determineScreen]);

  useEffect(() => {
    if (loadingRemote || !hasInitialized) {
      return undefined;
    }

    const runUpdateCheck = async () => {
      const currentBackend = await window.electronAPI.kobold.getCurrentBackend();
      if (!currentBackend) {
        return;
      }

      void checkForUpdates();
    };

    const initialTimeout = setTimeout(() => {
      void runUpdateCheck();
    }, 5000);

    const interval = setInterval(
      () => {
        void runUpdateCheck();
      },
      6 * 60 * 60 * 1000,
    );

    return () => {
      clearTimeout(initialTimeout);
      clearInterval(interval);
    };
  }, [loadingRemote, hasInitialized, checkForUpdates]);

  const handleBinaryUpdate = async (download: DownloadItem) => {
    const currentBackend = await window.electronAPI.kobold.getCurrentBackend();

    await handleDownload({
      isUpdate: true,
      item: download,
      oldBackendPath: currentBackend?.path,
      wasCurrentBinary: true,
    });

    closeModal();
  };

  const handleEject = async () => {
    const skipEjectConfirmation = await window.electronAPI.config.get('skipEjectConfirmation');

    if (skipEjectConfirmation) {
      performEject();
    } else {
      setEjectConfirmModalOpen(true);
    }
  };

  const handleEjectConfirm = (skipConfirmation: boolean) => {
    if (skipConfirmation) {
      window.electronAPI.config.set('skipEjectConfirmation', true);
    }
    performEject();
  };

  const handleWelcomeComplete = async () => {
    window.electronAPI.config.set('hasSeenWelcome', true);

    const currentBackend = await window.electronAPI.kobold.getCurrentBackend();
    determineScreen(currentBackend, true);
  };

  const handleDownloadComplete = () => setCurrentScreen('launch');
  const handleLaunch = () => {
    setActiveInterfaceTab('terminal');
    setCurrentScreen('interface');
  };

  return (
    <AppShell
      header={{ height: TITLEBAR_HEIGHT }}
      footer={{ height: STATUSBAR_HEIGHT }}
      padding={isInterfaceScreen ? 0 : 'md'}
    >
      <TitleBar
        currentScreen={currentScreen ?? 'launch'}
        currentTab={activeInterfaceTab}
        onEject={() => void handleEject()}
        onTabChange={setActiveInterfaceTab}
      />

      <AppShell.Main
        top={TITLEBAR_HEIGHT}
        style={{
          height: `calc(100vh - ${TITLEBAR_HEIGHT} - ${STATUSBAR_HEIGHT})`,
          minHeight: `calc(100vh - ${TITLEBAR_HEIGHT} - ${STATUSBAR_HEIGHT})`,
          overflow: 'auto',
          paddingBottom: 0,
          paddingLeft: isInterfaceScreen ? 0 : '.5rem',
          paddingRight: isInterfaceScreen ? 0 : '.5rem',
          paddingTop: 0,
          position: 'relative',
          top: TITLEBAR_HEIGHT,
        }}
      >
        <ErrorBoundary>
          {currentScreen === null ? (
            <Center h="100%" style={{ minHeight: '25rem' }}>
              <Stack align="center" gap="lg">
                <Loader size="xl" type="dots" />
                <Text c="dimmed" size="lg">
                  Loading...
                </Text>
              </Stack>
            </Center>
          ) : (
            <AppRouter
              currentScreen={currentScreen}
              hasInitialized={hasInitialized}
              activeInterfaceTab={activeInterfaceTab}
              isServerReady={isServerReady}
              onWelcomeComplete={() => void handleWelcomeComplete()}
              onDownloadComplete={() => handleDownloadComplete()}
              onLaunch={handleLaunch}
            />
          )}
        </ErrorBoundary>

        <UpdateAvailableModal
          opened={showUpdateModal && Boolean(binaryUpdateInfo)}
          onClose={closeModal}
          onSkip={skipUpdate}
          updateInfo={binaryUpdateInfo ?? undefined}
          onUpdate={handleBinaryUpdate}
        />
      </AppShell.Main>

      <StatusBar />

      <EjectConfirmModal
        opened={ejectConfirmModalOpen}
        onClose={() => setEjectConfirmModalOpen(false)}
        onConfirm={handleEjectConfirm}
      />

      <BackendCrashModal
        opened={crashInfo !== null}
        onClose={() => setCrashInfo(null)}
        crashInfo={crashInfo}
      />

      <NotepadContainer />
    </AppShell>
  );
};
