import { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { getOrCreateClientId } from './app/main/clientIdentity';
import { reportClientError } from './app/main/errorLogApi';
import { AppShell } from './app/main';
import { InitialLoadingOverlay } from './app/main/InitialLoadingOverlay';
import { ReleasePendingMainModal } from './app/main/ReleasePendingMainModal';
import { bindViewportCssVars } from './app/main/viewportCssVars';
import { reportVisitorPageView } from './features/history/api';
import { useAppStore } from './store';

const CHUNK_LOAD_RETRY_SESSION_KEY = 'ai-code-app.chunk-load-retried';
const INITIAL_LOADING_MIN_VISIBLE_MS = 450;

function shouldRetryChunkLoad(errorMessage: string) {
  return /Failed to fetch dynamically imported module|Importing a module script failed|Load failed|ChunkLoadError/i.test(
    errorMessage,
  );
}

function retryChunkLoadOnce(errorMessage: string) {
  if (typeof window === 'undefined' || typeof sessionStorage === 'undefined') {
    return false;
  }

  if (!shouldRetryChunkLoad(errorMessage)) {
    return false;
  }

  if (sessionStorage.getItem(CHUNK_LOAD_RETRY_SESSION_KEY) === '1') {
    return false;
  }

  sessionStorage.setItem(CHUNK_LOAD_RETRY_SESSION_KEY, '1');
  window.location.reload();
  return true;
}

function App() {
  const { currentPage } = useAppStore();
  const lastTrackedPageIdRef = useRef<string | null>(null);
  const [showInitialLoading, setShowInitialLoading] = useState(true);

  useLayoutEffect(() => bindViewportCssVars(), []);

  useEffect(() => {
    if (typeof window === 'undefined') {
      return undefined;
    }

    const handleError = (event: ErrorEvent) => {
      const reportedError = event.error instanceof Error ? event.error : null;
      const errorMessage = event.message || reportedError?.message || '클라이언트 오류가 발생했습니다.';

      if (retryChunkLoadOnce(errorMessage)) {
        return;
      }

      void reportClientError({
        errorType: 'window.error',
        errorName: reportedError?.name ?? null,
        errorMessage,
        stackTrace: reportedError?.stack ?? null,
        requestPath: `${window.location.pathname}${window.location.search}${window.location.hash}`,
        context: {
          filename: event.filename || null,
          line: event.lineno || null,
          column: event.colno || null,
        },
      });
    };

    const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
      const reason = event.reason;
      const reportedError = reason instanceof Error ? reason : null;
      const errorMessage =
        reportedError?.message || (typeof reason === 'string' ? reason : '처리되지 않은 Promise 거절이 발생했습니다.');

      if (retryChunkLoadOnce(errorMessage)) {
        return;
      }

      void reportClientError({
        errorType: 'unhandledrejection',
        errorName: reportedError?.name ?? null,
        errorMessage,
        stackTrace: reportedError?.stack ?? null,
        requestPath: `${window.location.pathname}${window.location.search}${window.location.hash}`,
        context: {
          reasonType: typeof reason,
        },
      });
    };

    window.addEventListener('error', handleError);
    window.addEventListener('unhandledrejection', handleUnhandledRejection);

    return () => {
      window.removeEventListener('error', handleError);
      window.removeEventListener('unhandledrejection', handleUnhandledRejection);
    };
  }, []);

  useEffect(() => {
    getOrCreateClientId();
  }, []);

  useEffect(() => {
    const hideTimer = window.setTimeout(() => {
      setShowInitialLoading(false);
    }, INITIAL_LOADING_MIN_VISIBLE_MS);

    return () => {
      window.clearTimeout(hideTimer);
    };
  }, []);

  useEffect(() => {
    if (lastTrackedPageIdRef.current === currentPage.id) {
      return;
    }

    lastTrackedPageIdRef.current = currentPage.id;
    void reportVisitorPageView(currentPage);
  }, [currentPage]);

  return (
    <>
      <AppShell />
      <ReleasePendingMainModal />
      {showInitialLoading ? <InitialLoadingOverlay /> : null}
    </>
  );
}

export default App;
