"use client";

import { isAxiosError } from "axios";
import {
  AlertCircle,
  AlertTriangle,
  CheckCircle2,
  ChevronRight,
  Globe,
  Loader2,
  RefreshCw,
  ShieldCheck,
} from "lucide-react";
import Image from "next/image";
import { useRouter, useSearchParams } from "next/navigation";
import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import { useTranslation } from "react-i18next";

import { getPublicCurrencies } from "@/api/currency";
import { getPlayerMe, getPlayerPing } from "@/api/player";
import { isInIframe } from "@/components/iframe-bridge";
import { LanguageSwitcher } from "@/components/language-switcher";
import { EntryHeroBanner } from "@/features/player/entry-hero-banner";
import { Button, buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { usePlayerSessionStore } from "@/stores/player-session-store";
import { LotteryApiBizError } from "@/types/api/errors";

const RETRY_ATTEMPTS = 3;
const RETRY_DELAY_MS = 2000;
/** 嵌入主站 iframe 时，等待父页 postMessage 下发 token 的最长时间 */
const IFRAME_TOKEN_WAIT_MS = 15_000;

const MAIN_SITE_URL = process.env.NEXT_PUBLIC_MAIN_SITE_URL?.trim() ?? "";

/** `/api/player/me` 等业务信封业务码 — 玩家 SSO 段 8001–8005（与后端 ErrorCode 一致） */
const PLAYER_BIZ_DETAIL: Partial<Record<number, string>> = {
  8001: "errors.player8001",
  8002: "errors.player8002",
  8003: "errors.player8003",
  8004: "errors.player8004",
  8005: "errors.player8005",
};

type EntryStepId = "token" | "account" | "hall";

type EntryStepStatus = "pending" | "in-progress" | "done" | "error";

type EntryStep = {
  id: EntryStepId;
  status: EntryStepStatus;
};

type Phase = "loading" | "success" | "failed";

type FailureRow = {
  code?: string;
  /** `entry` 命名空间下的 key，例如 `errors.noTokenDetail` */
  detailKey?: string;
  /** 服务端或动态错误兜底 */
  fallbackMessage?: string;
};

function sleep(ms: number): Promise<void> {
  return new Promise((r) => setTimeout(r, ms));
}

function initialSteps(): EntryStep[] {
  return [
    { id: "token", status: "pending" },
    { id: "account", status: "pending" },
    { id: "hall", status: "pending" },
  ];
}

function stripSearchParamFromBrowserUrl(name: string): void {
  if (typeof window === "undefined") return;
  const url = new URL(window.location.href);
  if (!url.searchParams.has(name)) return;
  url.searchParams.delete(name);
  const next = `${url.pathname}${url.search}${url.hash}`;
  window.history.replaceState(null, "", next);
}

function shouldRetryEntryRequest(error: unknown): boolean {
  if (error instanceof LotteryApiBizError) {
    return false;
  }
  if (isAxiosError(error)) {
    if (error.code === "ECONNABORTED") {
      return true;
    }
    if (!error.response) {
      return true;
    }
    if (error.response.status >= 500) {
      return true;
    }
  }
  return false;
}

export function EntryGate() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const { t } = useTranslation("entry");
  const { t: tc } = useTranslation("common");

  const tokenFromUrl = searchParams.get("token") ?? "";
  const sessionExpired = searchParams.get("session") === "expired";

  const { bearerToken, setBearerToken, setProfile, setCurrencies, clearBearerToken } =
    usePlayerSessionStore();
  /** 仅主站 iframe 内复用已有 token；直连站点只认 URL `?token=`（SSO） */
  const isReturningSession =
    typeof window !== "undefined" &&
    isInIframe() &&
    !sessionExpired &&
    !tokenFromUrl &&
    (bearerToken ?? "").trim() !== "";
  const [resumeSilent, setResumeSilent] = useState(isReturningSession);
  const waitingForEmbeddedToken =
    !sessionExpired &&
    typeof window !== "undefined" &&
    isInIframe() &&
    !tokenFromUrl &&
    !(bearerToken ?? "").trim();

  const gateReady = useMemo(() => {
    if (typeof window === "undefined") return false;

    if (!isInIframe()) {
      if (sessionExpired) return false;
      return tokenFromUrl !== "";
    }

    return true;
  }, [sessionExpired, tokenFromUrl]);

  useEffect(() => {
    if (gateReady) return;
    if (typeof window === "undefined") return;
    if (isInIframe()) return;

    if (sessionExpired) {
      router.replace("/login?session=expired");
    } else if (!tokenFromUrl) {
      router.replace("/login");
    }
  }, [gateReady, router, sessionExpired, tokenFromUrl]);

  const [phase, setPhase] = useState<Phase>(sessionExpired ? "failed" : "loading");
  const [failureDetails, setFailureDetails] = useState<FailureRow[]>(() =>
    sessionExpired
      ? [
          {
            code: "SESSION_EXPIRED",
            detailKey: "errors.sessionExpiredDetail",
          },
        ]
      : [],
  );
  const [steps, setSteps] = useState<EntryStep[]>(initialSteps());

  const entryToken = useMemo(() => {
    if (typeof window === "undefined") {
      return tokenFromUrl || bearerToken;
    }
    if (!isInIframe()) {
      return tokenFromUrl;
    }
    return tokenFromUrl || bearerToken;
  }, [bearerToken, tokenFromUrl]);
  /** 防止 token 写入 store / URL 剥离后重复触发进场，避免成功/失败页闪一下 */
  const entryLifecycleRef = useRef<"idle" | "running" | "done">("idle");

  const updateStep = useCallback((stepId: EntryStepId, status: EntryStepStatus) => {
    setSteps((prev) => prev.map((s) => (s.id === stepId ? { ...s, status } : s)));
  }, []);

  const progress = useMemo(() => {
    const doneCount = steps.filter((s) => s.status === "done").length;
    const inProgressCount = steps.filter((s) => s.status === "in-progress").length;
    return Math.round(((doneCount + inProgressCount * 0.5) / steps.length) * 100);
  }, [steps]);

  const doEntry = useCallback(async () => {
    if (entryLifecycleRef.current === "running" || entryLifecycleRef.current === "done") {
      return;
    }

    if (!entryToken) {
      // 主站 iframe：token 由 MAIN_INIT_TOKEN 稍后到达，勿先闪「授权失败」
      if (typeof window !== "undefined" && isInIframe() && !tokenFromUrl) {
        return;
      }
      if (typeof window !== "undefined" && !isInIframe()) {
        router.replace("/login");
        return;
      }
      entryLifecycleRef.current = "done";
      setPhase("failed");
      setFailureDetails([{ code: "NO_TOKEN", detailKey: "errors.noTokenDetail" }]);
      return;
    }

    entryLifecycleRef.current = "running";
    setResumeSilent(false);
    setPhase("loading");
    setFailureDetails([]);

    if (tokenFromUrl) {
      setBearerToken(tokenFromUrl);
      stripSearchParamFromBrowserUrl("token");
    }

    setSteps((prev) =>
      prev.map((s) => (s.id === "token" ? { ...s, status: "in-progress" } : s)),
    );

    await sleep(500);

    let lastError: unknown = null;

    for (let attempt = 1; attempt <= RETRY_ATTEMPTS; attempt++) {
      try {
        const [me] = await Promise.all([getPlayerMe(), sleep(300)]);

        try {
          const currencies = await getPublicCurrencies();
          setCurrencies(currencies.items);
        } catch {
          // 不阻断进场主流程；金额精度会退回默认 2 位兜底。
        }

        updateStep("token", "done");
        updateStep("account", "done");

        setSteps((prev) =>
          prev.map((s) => (s.id === "hall" ? { ...s, status: "in-progress" } : s)),
        );

        await Promise.all([getPlayerPing(), sleep(300)]);

        updateStep("hall", "done");
        setProfile(me);
        entryLifecycleRef.current = "done";
        router.replace("/hall");
        return;
      } catch (err) {
        lastError = err;

        if (err instanceof LotteryApiBizError) {
          entryLifecycleRef.current = "done";
          updateStep("token", "error");
          setPhase("failed");

          const details: FailureRow[] = [];
          if (typeof err.code === "number") {
            const dk = PLAYER_BIZ_DETAIL[err.code];
            details.push({
              code: String(err.code),
              ...(dk ? { detailKey: dk } : {}),
              fallbackMessage:
                typeof err.message === "string" ? err.message : undefined,
            });
          }

          const rows =
            details.length > 0
              ? details
              : [
                  {
                    fallbackMessage: err.message ?? t("errors.unknown"),
                  },
                ];

          setFailureDetails(rows);
          clearBearerToken();
          return;
        }

        if (!shouldRetryEntryRequest(err)) {
          entryLifecycleRef.current = "done";
          updateStep("token", "error");
          setPhase("failed");
          setFailureDetails([{ code: "NETWORK_ERROR", detailKey: "errors.networkDetail" }]);
          return;
        }

        if (attempt < RETRY_ATTEMPTS) {
          await sleep(RETRY_DELAY_MS);
        }
      }
    }

    entryLifecycleRef.current = "done";
    updateStep("token", "error");
    setPhase("failed");
    setFailureDetails([
      { code: "MAX_RETRIES", detailKey: "errors.maxRetriesDetail" },
      {
        fallbackMessage:
          lastError instanceof Error ? lastError.message : t("errors.tryLater"),
      },
    ]);
  }, [
    entryToken,
    tokenFromUrl,
    setBearerToken,
    setProfile,
    setCurrencies,
    clearBearerToken,
    router,
    updateStep,
    t,
  ]);

  const handleRetry = useCallback(() => {
    entryLifecycleRef.current = "idle";
    setPhase("loading");
    setFailureDetails([]);
    setSteps(initialSteps());
    void doEntry();
  }, [doEntry]);

  useEffect(() => {
    if (!sessionExpired) return;
    clearBearerToken();
    stripSearchParamFromBrowserUrl("session");
  }, [sessionExpired, clearBearerToken]);

  const trimmedEntryToken = (entryToken ?? "").trim();
  const doEntryRef = useRef(doEntry);
  useEffect(() => {
    doEntryRef.current = doEntry;
  }, [doEntry]);

  useEffect(() => {
    if (sessionExpired || waitingForEmbeddedToken || isReturningSession) return;
    if (entryLifecycleRef.current !== "idle") return;
    if (!trimmedEntryToken) return;

    const tmr = window.setTimeout(() => {
      void doEntryRef.current();
    }, 300);
    return () => window.clearTimeout(tmr);
  }, [isReturningSession, sessionExpired, trimmedEntryToken, waitingForEmbeddedToken]);

  useEffect(() => {
    if (!isReturningSession) return;
    if (entryLifecycleRef.current === "running" || entryLifecycleRef.current === "done") {
      return;
    }

    entryLifecycleRef.current = "running";
    let cancelled = false;

    const resumeToHall = async () => {
      try {
        if (!usePlayerSessionStore.getState().profile) {
          const [me] = await Promise.all([getPlayerMe(), sleep(300)]);
          if (cancelled) return;
          try {
            const currencies = await getPublicCurrencies();
            setCurrencies(currencies.items);
          } catch {
            /* 不阻断回大厅 */
          }
          setProfile(me);
          await Promise.all([getPlayerPing(), sleep(200)]);
          if (cancelled) return;
        }
        entryLifecycleRef.current = "done";
        router.replace("/hall");
      } catch (err) {
        if (cancelled) return;
        if (err instanceof LotteryApiBizError) {
          entryLifecycleRef.current = "done";
          clearBearerToken();
          router.replace("/login");
          return;
        }
        entryLifecycleRef.current = "idle";
        setResumeSilent(false);
        void doEntryRef.current();
      }
    };

    void resumeToHall();
    return () => {
      cancelled = true;
      if (entryLifecycleRef.current === "running") {
        entryLifecycleRef.current = "idle";
      }
    };
  }, [clearBearerToken, isReturningSession, router, setCurrencies, setProfile]);

  useEffect(() => {
    if (sessionExpired) return;
    if (tokenFromUrl || bearerToken) return;
    if (typeof window === "undefined" || !isInIframe()) return;

    const tmr = window.setTimeout(() => {
      if (usePlayerSessionStore.getState().bearerToken?.trim()) return;
      if (entryLifecycleRef.current !== "idle") return;
      entryLifecycleRef.current = "done";
      setFailureDetails([{ code: "NO_TOKEN", detailKey: "errors.noTokenDetail" }]);
      setPhase("failed");
    }, IFRAME_TOKEN_WAIT_MS);

    return () => window.clearTimeout(tmr);
  }, [bearerToken, sessionExpired, tokenFromUrl]);

  if (!gateReady) {
    return (
      <div className="flex min-h-0 flex-1 items-center justify-center bg-white">
        <Loader2 className="size-8 animate-spin text-red-600" aria-hidden />
      </div>
    );
  }

  if (resumeSilent && phase === "loading") {
    return <EntryBusyScreen />;
  }

  return (
    <div className="relative flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-y-contain bg-white">
      <div className="absolute left-0 right-0 top-0 z-20 hidden items-center px-4 py-3 lg:flex">
        <LanguageSwitcher variant="header" showFlag={false} />
      </div>

      <EntryHeroBanner
        src={phase === "success" ? "/entry/image4.png" : "/entry/image1.png"}
        alt={t("header.backgroundAlt")}
        className={phase === "success" ? "bg-white" : "bg-red-600"}
      >
        <div className="absolute left-0 right-0 top-0 z-20 flex items-center px-4 py-3 lg:hidden">
          <LanguageSwitcher variant="header" showFlag={false} />
        </div>
      </EntryHeroBanner>

      <div className="flex flex-1 flex-col px-3 py-5 lg:justify-center lg:overflow-y-auto lg:px-10 lg:py-8">
        {phase === "loading" || waitingForEmbeddedToken ? (
          <div className="mx-auto w-full max-w-md lg:max-w-lg">
            <div className="mb-6 flex items-center gap-2">
              <div className="flex size-8 items-center justify-center rounded bg-red-600 text-white">
                <Globe className="size-5" aria-hidden />
              </div>
              <span className="font-medium text-gray-800">{t("loading.title")}</span>
            </div>

            <div className="mb-6">
              <div className="mb-2 flex justify-between text-xs text-gray-500">
                <span>{t("loading.progress")}</span>
                <span className="font-medium text-red-600">{progress}%</span>
              </div>
              <div className="h-2 overflow-hidden rounded-full bg-gray-200">
                <div
                  className="h-full rounded-full bg-red-600 transition-all duration-500"
                  style={{ width: `${progress}%` }}
                />
              </div>
            </div>

            <div className="space-y-3">
              {steps.map((step) => (
                <div key={step.id} className="flex items-start gap-3">
                  <div
                    className={cn(
                      "flex size-8 shrink-0 items-center justify-center rounded-full border-2",
                      step.status === "done" &&
                        "border-green-500 bg-green-500 text-white",
                      step.status === "in-progress" &&
                        "border-blue-600 bg-blue-600 text-white",
                      step.status === "pending" &&
                        "border-gray-300 bg-gray-100 text-gray-400",
                      step.status === "error" && "border-red-500 bg-red-500 text-white",
                    )}
                  >
                    {step.status === "done" ? (
                      <CheckCircle2 className="size-4" aria-hidden />
                    ) : null}
                    {step.status === "in-progress" ? (
                      <Loader2 className="size-4 animate-spin" aria-hidden />
                    ) : null}
                    {step.status === "pending" ? (
                      <div className="size-2 rounded-full bg-gray-400" aria-hidden />
                    ) : null}
                    {step.status === "error" ? (
                      <AlertCircle className="size-4" aria-hidden />
                    ) : null}
                  </div>
                  <div className="flex-1">
                    <div className="flex items-center justify-between">
                      <span
                        className={cn(
                          "font-medium",
                          step.status === "done" && "text-green-600",
                          step.status === "in-progress" && "text-blue-600",
                          step.status === "pending" && "text-gray-600",
                          step.status === "error" && "text-red-600",
                        )}
                      >
                        {t(`steps.${step.id}.title`)}
                      </span>
                      <EntryStatusBadge status={step.status} />
                    </div>
                    <p className="text-xs text-gray-500">
                      {t(`steps.${step.id}.description`)}
                    </p>
                  </div>
                </div>
              ))}
            </div>
          </div>
        ) : null}

        {phase === "failed" ? (
          <div className="mx-auto w-full max-w-md lg:max-w-lg">
            <div className="mb-6 text-center">
              <div className="mx-auto mb-4 flex size-16 items-center justify-center rounded-full bg-red-100 text-red-600">
                <AlertTriangle className="size-8" aria-hidden />
              </div>
              <h2 className="mb-1 text-xl font-bold text-red-600">{t("failure.title")}</h2>
              <p className="text-sm text-gray-600">{t("failure.subtitle")}</p>
            </div>

            {failureDetails.length > 0 ? (
              <div className="mb-6 overflow-hidden rounded-lg border border-red-200 bg-red-50">
                <div className="border-b border-red-200 bg-red-100 px-4 py-2">
                  <span className="text-sm font-medium text-red-800">
                    {t("failure.detailsTitle")}
                  </span>
                </div>
                <div className="overflow-x-auto overscroll-x-contain [-webkit-overflow-scrolling:touch]">
                <table className="w-full min-w-[20rem] text-sm">
                  <thead className="bg-red-100/50 text-xs">
                    <tr>
                      <th className="px-3 py-2 text-left font-medium text-red-700">
                        {t("failure.table.no")}
                      </th>
                      <th className="px-3 py-2 text-left font-medium text-red-700">
                        {t("failure.table.check")}
                      </th>
                      <th className="px-3 py-2 text-left font-medium text-red-700">
                        {t("failure.table.reason")}
                      </th>
                    </tr>
                  </thead>
                  <tbody>
                    {failureDetails.map((detail, idx) => (
                      <tr key={`${detail.code}-${idx}`} className="border-t border-red-100">
                        <td className="px-3 py-2 text-gray-600">{idx + 1}</td>
                        <td className="px-3 py-2 text-gray-800">
                          {detail.code ?? tc("errors.general")}
                        </td>
                        <td className="px-3 py-2 text-gray-600">
                          {detail.detailKey
                            ? t(detail.detailKey)
                            : (detail.fallbackMessage ?? t("errors.unknown"))}
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
                </div>
              </div>
            ) : null}

            <div className="flex flex-col gap-2">
              <Button
                onClick={handleRetry}
                className="w-full gap-2 bg-red-600 text-white hover:bg-red-700"
                size="lg"
                type="button"
              >
                <RefreshCw className="size-4" aria-hidden />
                {t("failure.reenter")}
              </Button>
              {MAIN_SITE_URL !== "" ? (
                <a
                  href={MAIN_SITE_URL}
                  target="_top"
                  rel="noopener noreferrer"
                  className={cn(buttonVariants({ variant: "outline", size: "lg" }), "w-full")}
                >
                  {t("failure.backToMainSite")}
                </a>
              ) : null}
            </div>
          </div>
        ) : null}

        {phase === "success" ? (
          <div className="mx-auto w-full max-w-md text-center lg:max-w-lg">
            <div className="mb-6">
              <div className="mx-auto mb-4 flex size-16 items-center justify-center rounded-full bg-green-100 text-green-600">
                <CheckCircle2 className="size-8" aria-hidden />
              </div>
              <h2 className="mb-1 text-xl font-bold text-green-600">
                {t("success.title")}
              </h2>
              <p className="text-sm text-gray-600">{t("success.subtitle")}</p>
            </div>

            <div className="mb-6 space-y-3">
              {steps.map((step) => (
                <div key={step.id} className="flex items-center gap-3">
                  <div className="flex size-6 items-center justify-center rounded-full bg-green-500 text-white">
                    <CheckCircle2 className="size-4" aria-hidden />
                  </div>
                  <span className="flex-1 text-left font-medium text-gray-700">
                    {t(`steps.${step.id}.title`)}
                  </span>
                  <span className="text-xs text-green-600">{t("success.doneLabel")}</span>
                  <CheckCircle2 className="size-4 text-green-500" aria-hidden />
                </div>
              ))}
            </div>

            <Button
              onClick={() => router.push("/hall")}
              className="w-full gap-2 bg-blue-600 text-white hover:bg-blue-700"
              size="lg"
              type="button"
            >
              {t("success.continue")}
              <ChevronRight className="size-4" aria-hidden />
            </Button>
          </div>
        ) : null}
      </div>

      <div className="relative min-h-[150px] overflow-hidden px-4 pb-8 pt-16">
        <Image
          src="/entry/image2.png"
          alt=""
          fill
          className="pointer-events-none object-cover object-bottom"
          aria-hidden
        />
        <div className="relative z-10 flex items-center justify-center gap-2 text-xs font-medium text-gray-600">
          <ShieldCheck className="size-4 text-red-500" aria-hidden />
          <span>{t("footer.secure")}</span>
        </div>
      </div>
    </div>
  );
}

function EntryBusyScreen() {
  const { t } = useTranslation("entry");

  return (
    <div className="flex min-h-0 flex-1 flex-col items-center justify-center gap-3 bg-white px-6">
      <Loader2 className="size-10 animate-spin text-red-600" aria-hidden />
      <p className="text-sm font-medium text-slate-600">{t("loading.title")}</p>
    </div>
  );
}

function EntryStatusBadge({ status }: { status: EntryStepStatus }) {
  const { t } = useTranslation("common");

  if (status === "done") {
    return (
      <span className="flex items-center gap-1 rounded bg-green-100 px-2 py-0.5 text-xs font-medium text-green-700">
        {t("status.done")}
        <CheckCircle2 className="size-3" aria-hidden />
      </span>
    );
  }
  if (status === "in-progress") {
    return (
      <span className="flex items-center gap-1 rounded bg-blue-100 px-2 py-0.5 text-xs font-medium text-blue-700">
        {t("status.inProgress")}
        <Loader2 className="size-3 animate-spin" aria-hidden />
      </span>
    );
  }
  if (status === "pending") {
    return (
      <span className="rounded bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-500">
        {t("status.pending")}
      </span>
    );
  }
  return (
    <span className="rounded bg-red-100 px-2 py-0.5 text-xs font-medium text-red-700">
      {t("status.failed")}
    </span>
  );
}
