import { App as AntdApp } from 'antd';
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 { buildChatPath } from './app/main/routes';
import { isPreviewRuntime } from './app/main/previewRuntime';
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 CACHE_RECOVERY_SESSION_KEY = 'ai-code-app.cache-recovery-completed';
const INITIAL_LOADING_MIN_VISIBLE_MS = 450;
const CACHE_RECOVERY_NOTICE = '캐시된 화면 정보가 맞지 않아 홈으로 이동합니다. 다시 열어 주세요.';
const CACHE_RECOVERY_DELAY_MS = 900;

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

function shouldRecoverFromCacheError(errorMessage: string) {
  return /Failed to fetch dynamically imported module|Importing a module script failed|Load failed|ChunkLoadError|Loading chunk|Failed to load module script|does not provide an export named|Cannot find module/i.test(
    errorMessage,
  );
}

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

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

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

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

function getHomeRecoveryUrl() {
  if (typeof window === 'undefined') {
    return buildChatPath('live');
  }

  return new URL(buildChatPath('live'), window.location.origin).toString();
}

function tryRecoverToHomeFromCacheError(errorMessage: string, notify: (text: string) => void) {
  if (typeof window === 'undefined' || typeof sessionStorage === 'undefined') {
    return false;
  }

  if (isPreviewRuntime()) {
    return false;
  }

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

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

    sessionStorage.setItem(CACHE_RECOVERY_SESSION_KEY, '1');
    notify(CACHE_RECOVERY_NOTICE);
    window.setTimeout(() => {
      window.location.replace(getHomeRecoveryUrl());
    }, CACHE_RECOVERY_DELAY_MS);
    return true;
  } catch {
    return false;
  }
}

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

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

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

    const notifyCacheRecovery = (text: string) => {
      message.warning({
        content: text,
        duration: 1.5,
      });
    };

    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,
        },
      });

      tryRecoverToHomeFromCacheError(errorMessage, notifyCacheRecovery);
    };

    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,
        },
      });

      tryRecoverToHomeFromCacheError(errorMessage, notifyCacheRecovery);
    };

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

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

  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;
