import { Component } from "react";
import styles from "./ErrorBoundary.module.css";

const ASSET_RELOAD_STORAGE_PREFIX = "hyapp-admin:asset-reload:";
const ASSET_RELOAD_RETRY_MS = 5 * 60 * 1000;
const ASSET_ERROR_PATTERNS = [
  /Unable to preload CSS/i,
  /Failed to fetch dynamically imported module/i,
  /error loading dynamically imported module/i,
  /Importing a module script failed/i,
  /Load failed for the module/i,
  /ChunkLoadError/i,
  /CSS_CHUNK_LOAD_FAILED/i,
];

export function isRecoverableAssetLoadError(error) {
  const message = errorMessage(error);
  return ASSET_ERROR_PATTERNS.some((pattern) => pattern.test(message));
}

export function assetReloadStorageKey(error, location = window.location) {
  const message = errorMessage(error);
  const assetMatch = message.match(/\/assets\/[^\s"')]+/);
  const asset = assetMatch?.[0] || message.slice(0, 160) || "unknown";
  return `${ASSET_RELOAD_STORAGE_PREFIX}${location.pathname}:${asset}`;
}

function errorMessage(error) {
  if (!error) {
    return "";
  }

  if (typeof error === "string") {
    return error;
  }

  return [error.name, error.message, error.stack].filter(Boolean).join("\n");
}

function canRetryAssetReload(error) {
  const key = assetReloadStorageKey(error);
  const now = Date.now();

  try {
    const previous = Number(window.sessionStorage.getItem(key) || "0");
    if (previous && now - previous < ASSET_RELOAD_RETRY_MS) {
      return false;
    }

    window.sessionStorage.setItem(key, String(now));
    return true;
  } catch {
    if (window.__hyappAssetReloadAttempted) {
      return false;
    }
    window.__hyappAssetReloadAttempted = true;
    return true;
  }
}

function scheduleAssetReload(error) {
  if (!isRecoverableAssetLoadError(error) || !canRetryAssetReload(error)) {
    return false;
  }

  window.setTimeout(() => {
    window.location.reload();
  }, 50);
  return true;
}

export class ErrorBoundary extends Component {
  state = { error: null, recovering: false };

  static getDerivedStateFromError(error) {
    return { error, recovering: isRecoverableAssetLoadError(error) };
  }

  componentDidMount() {
    window.addEventListener("vite:preloadError", this.handlePreloadError);
  }

  componentWillUnmount() {
    window.removeEventListener("vite:preloadError", this.handlePreloadError);
  }

  componentDidCatch(error) {
    if (!scheduleAssetReload(error) && isRecoverableAssetLoadError(error)) {
      this.setState({ recovering: false });
    }
  }

  handlePreloadError = (event) => {
    const error = event.payload || event.reason || event.error;
    if (!scheduleAssetReload(error)) {
      return;
    }

    event.preventDefault();
    this.setState({ error, recovering: true });
  };

  reset = () => {
    this.setState({ error: null, recovering: false });
  };

  reload = () => {
    window.location.reload();
  };

  render() {
    if (!this.state.error) {
      return this.props.children;
    }

    if (this.state.recovering) {
      return (
        <main className={styles.root}>
          <section className={styles.panel}>
            <h1 className={styles.title}>正在刷新页面</h1>
            <p className={styles.message}>正在加载最新版本</p>
          </section>
        </main>
      );
    }

    return (
      <main className={styles.root}>
        <section className={styles.panel}>
          <h1 className={styles.title}>页面加载失败</h1>
          <p className={styles.message}>{this.state.error.message || "请刷新后重试"}</p>
          <div className={styles.actions}>
            <button className={styles.secondaryButton} type="button" onClick={this.reset}>
              返回页面
            </button>
            <button className={styles.primaryButton} type="button" onClick={this.reload}>
              刷新
            </button>
          </div>
        </section>
      </main>
    );
  }
}
