import { IconAlertTriangle } from '@douyinfe/semi-icons';
import { Component, type ElementType, type ErrorInfo, type ReactNode, Suspense, useCallback, useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { PageLoading } from '../shared/AsyncState';
import { RecoveryActions } from '../shared/RecoveryActions';

const CHUNK_RELOAD_KEY = 'vehicle-platform:route-chunk-reload';
const CHUNK_RELOAD_COOLDOWN_MS = 2 * 60_000;

export function isRouteChunkError(error: unknown) {
  const message = error instanceof Error ? `${error.name} ${error.message}` : String(error);
  return /ChunkLoadError|RouteChunkLoadTimeoutError|Route chunk load timed out|Loading chunk .+ failed|Failed to fetch dynamically imported module|Importing a module script failed|error loading dynamically imported module/i.test(message);
}

function isRouteChunkTimeout(error: unknown) {
  return error instanceof Error && (error.name === 'RouteChunkLoadTimeoutError' || /Route chunk load timed out/i.test(error.message));
}

function canAutoReload(routeKey: string) {
  try {
    const marker = JSON.parse(window.sessionStorage.getItem(CHUNK_RELOAD_KEY) ?? 'null') as { routeKey?: string; at?: number } | null;
    return !marker || marker.routeKey !== routeKey || Date.now() - (marker.at ?? 0) > CHUNK_RELOAD_COOLDOWN_MS;
  } catch {
    return true;
  }
}

function rememberAutoReload(routeKey: string) {
  try {
    window.sessionStorage.setItem(CHUNK_RELOAD_KEY, JSON.stringify({ routeKey, at: Date.now() }));
  } catch {
    // Recovery must still work when storage is unavailable.
  }
}

class RecoverableRouteBoundary extends Component<{ children: ReactNode; routeKey: string; retryKey: number; onRetry: () => void }, { error?: Error }> {
  state: { error?: Error } = {};

  static getDerivedStateFromError(error: Error) {
    return { error };
  }

  componentDidCatch(error: Error, _info: ErrorInfo) {
    if (isRouteChunkError(error) && canAutoReload(this.props.routeKey)) {
      rememberAutoReload(this.props.routeKey);
      window.location.reload();
    }
  }

  componentDidUpdate(previous: Readonly<{ children: ReactNode; routeKey: string; retryKey: number; onRetry: () => void }>) {
    if (this.state.error && (previous.routeKey !== this.props.routeKey || previous.retryKey !== this.props.retryKey)) {
      this.setState({ error: undefined });
    }
  }

  render() {
    const { error } = this.state;
    if (!error) return this.props.children;
    const chunkError = isRouteChunkError(error);
    const chunkTimeout = isRouteChunkTimeout(error);
    return <section className="v2-route-error" role="alert">
      <span><IconAlertTriangle /></span>
      <div>
        <small>{chunkTimeout ? '页面资源加载超时' : chunkError ? '检测到页面版本更新' : '页面运行异常'}</small>
        <h2>{chunkTimeout ? '当前模块没有在预期时间内加载完成' : chunkError ? '正在等待加载最新页面资源' : '当前模块暂时无法显示'}</h2>
        <p>{chunkTimeout ? '系统已等待并尝试恢复，可能是网络暂时不稳定。刷新后会重新加载当前模块，不会丢失服务端数据。' : chunkError ? '通常是发布后旧标签页仍引用上一版本资源。刷新后会恢复，不会丢失服务端数据。' : '页面已被安全隔离，侧栏和其他模块仍可使用。更换筛选条件会自动重试，也可刷新当前页面。'}</p>
        <code>{error.message || error.name}</code>
        <RecoveryActions
          primaryLabel="重试加载模块"
          onPrimary={this.props.onRetry}
          secondaryLabel="刷新当前页面"
          onSecondary={() => window.location.reload()}
        />
      </div>
    </section>;
  }
}

export class PlatformErrorBoundary extends Component<{ children: ReactNode }, { error?: Error }> {
  state: { error?: Error } = {};

  static getDerivedStateFromError(error: Error) {
    return { error };
  }

  render() {
    const { error } = this.state;
    if (!error) return this.props.children;
    return <section className="v2-route-error v2-root-error" role="alert">
      <span><IconAlertTriangle /></span>
      <div>
        <small>平台外壳运行异常</small>
        <h2>工作台暂时无法继续显示</h2>
        <p>系统已阻止异常扩散。请重新加载最新版本；服务端数据不会因页面恢复操作而改变。</p>
        <code>{error.message || error.name}</code>
        <RecoveryActions primaryLabel="重新加载平台" onPrimary={() => window.location.reload()} />
      </div>
    </section>;
  }
}

export function RoutePage({ page, recreatePage, label }: { page: ElementType; recreatePage?: () => ElementType; label: string }) {
  const location = useLocation();
  const routeKey = `${location.pathname}${location.search}`;
  const [retryKey, setRetryKey] = useState(0);
  const Page = useMemo(() => retryKey && recreatePage ? recreatePage() : page, [page, recreatePage, retryKey]);
  const retry = useCallback(() => setRetryKey((value) => value + 1), []);
  return <RecoverableRouteBoundary key={location.pathname} routeKey={routeKey} retryKey={retryKey} onRetry={retry}>
    <Suspense fallback={<PageLoading label={`正在加载${label}`} />}><Page key={retryKey} /></Suspense>
  </RecoverableRouteBoundary>;
}
