"use client";

import { isAxiosError } from "axios";
import {
  AlertCircle,
  AlertTriangle,
  CheckCircle2,
  ChevronRight,
  Globe,
  Loader2,
  ShieldCheck,
} from "lucide-react";
import Image from "next/image";
import { useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useMemo, 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 { 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();

  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 effectiveToken = tokenFromUrl || bearerToken;

  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 (!effectiveToken) {
      // 主站 iframe：token 由 MAIN_INIT_TOKEN 稍后到达，勿先闪「授权失败」
      if (typeof window !== "undefined" && isInIframe() && !tokenFromUrl) {
        return;
      }
      setPhase("failed");
      setFailureDetails([{ code: "NO_TOKEN", detailKey: "errors.noTokenDetail" }]);
      return;
    }

    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);
        setPhase("success");

        await sleep(600);
        router.replace("/hall");
        return;
      } catch (err) {
        lastError = err;

        if (err instanceof LotteryApiBizError) {
          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)) {
          updateStep("token", "error");
          setPhase("failed");
          setFailureDetails([{ code: "NETWORK_ERROR", detailKey: "errors.networkDetail" }]);
          return;
        }

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

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

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

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

  useEffect(() => {
    if (sessionExpired) return;

    const embedded = typeof window !== "undefined" && isInIframe() && !tokenFromUrl;
    if (embedded && !effectiveToken) {
      setPhase("loading");
      return;
    }

    const tmr = window.setTimeout(() => {
      void doEntry();
    }, 300);
    return () => window.clearTimeout(tmr);
  }, [doEntry, sessionExpired, effectiveToken, tokenFromUrl]);

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

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

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

  return (
    <div className="relative flex min-h-dvh flex-col bg-white">
      <div className={cn("relative h-[45vh] min-h-[320px]", phase === "success" ? "bg-white" : "bg-red-600")}>
        <div className="pointer-events-none absolute inset-0 overflow-hidden">
          <Image
            src={phase === "success" ? "/entry/image4.png" : "/entry/image1.png"}
            alt={t("header.backgroundAlt")}
            fill
            className="object-cover object-center"
            priority
          />
        </div>

        <div className="absolute left-0 right-0 top-0 z-20 flex items-center px-4 py-3">
          <LanguageSwitcher variant="header" showFlag={false} />
        </div>
      </div>

      <div className="flex flex-1 flex-col px-3 py-5">
        {phase === "loading" ? (
          <div className="mx-auto w-full max-w-md">
            <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">
            <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>
                <table className="w-full 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>
            ) : 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"
              >
                <Loader2 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">
            <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 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>
  );
}
