"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { Alert, Button, Checkbox, Input, Modal, Segmented } from "antd";
import { useI18n } from "@/i18n";
import { useLoginModalStore } from "@/store/loginModalStore";
import { useUserStore } from "@/store/userStore";

type LoginMode = "code" | "wechat" | "password";
type CodePurpose = "login" | "wechat-bind";

interface ApiResponse {
  data?: unknown;
  message?: unknown;
  status?: unknown;
}

const isRecord = (value: unknown): value is Record<string, unknown> =>
  typeof value === "object" && value !== null && !Array.isArray(value);

const getMessage = (payload: ApiResponse, fallback: string): string =>
  typeof payload.message === "string" && payload.message.trim()
    ? payload.message
    : fallback;

const getTokenFromResponse = (payload: ApiResponse): string | null => {
  if (isRecord(payload.data) && typeof payload.data.token === "string") {
    return payload.data.token.trim() || null;
  }
  return null;
};

async function readJsonResponse(response: Response, fallbackMessage: string): Promise<ApiResponse> {
  const payload = (await response.json()) as ApiResponse;
  if (!response.ok || payload.status !== "0000") {
    throw new Error(getMessage(payload, fallbackMessage));
  }
  return payload;
}

export function LoginModal() {
  const { t } = useI18n();
  const { isOpen, reason, closeLoginModal } = useLoginModalStore();
  const fetchUserInfo = useUserStore((state) => state.fetchUserInfo);

  const wxPollTimerRef = useRef<number | null>(null);

  const [mode, setMode] = useState<LoginMode>("code");
  const [phone, setPhone] = useState("");
  const [password, setPassword] = useState("");
  const [code, setCode] = useState("");
  const [inviteCode, setInviteCode] = useState("");
  const [countdown, setCountdown] = useState(0);
  const [isLoading, setIsLoading] = useState(false);
  const [isSendingCode, setIsSendingCode] = useState(false);
  const [error, setError] = useState("");
  const [agreementChecked, setAgreementChecked] = useState(false);

  const [captchaOpen, setCaptchaOpen] = useState(false);
  const [captchaId, setCaptchaId] = useState("");
  const [captchaImage, setCaptchaImage] = useState("");
  const [captchaValue, setCaptchaValue] = useState("");
  const [captchaLoading, setCaptchaLoading] = useState(false);
  const [captchaError, setCaptchaError] = useState("");
  const [codeTargetPhone, setCodeTargetPhone] = useState("");
  const [codePurpose, setCodePurpose] = useState<CodePurpose>("login");

  const [wxSceneCode, setWxSceneCode] = useState("");
  const [wxQrUrl, setWxQrUrl] = useState("");
  const [wxQrError, setWxQrError] = useState("");
  const [wxQrLoading, setWxQrLoading] = useState(false);
  const [wxNeedBindPhone, setWxNeedBindPhone] = useState(false);
  const [wxRegisterToken, setWxRegisterToken] = useState("");
  const [wxBindPhone, setWxBindPhone] = useState("");
  const [wxBindCode, setWxBindCode] = useState("");
  const [wxBindInviteCode, setWxBindInviteCode] = useState("");

  const stopWxPoll = useCallback((): void => {
    if (wxPollTimerRef.current !== null) {
      window.clearTimeout(wxPollTimerRef.current);
      wxPollTimerRef.current = null;
    }
  }, []);

  const clearWxState = useCallback((): void => {
    stopWxPoll();
    setWxSceneCode("");
    setWxQrUrl("");
    setWxQrError("");
    setWxQrLoading(false);
    setWxNeedBindPhone(false);
    setWxRegisterToken("");
    setWxBindPhone("");
    setWxBindCode("");
    setWxBindInviteCode("");
  }, [stopWxPoll]);

  const completeLoginWithToken = useCallback(
    async (token: string): Promise<void> => {
      const userInfo = await fetchUserInfo(token);
      if (!userInfo) {
        throw new Error(t("auth.loginUserInfoFailed"));
      }
      stopWxPoll();
      closeLoginModal();
      window.location.reload();
    },
    [closeLoginModal, fetchUserInfo, stopWxPoll, t]
  );

  useEffect(() => {
    if (!isOpen) {
      stopWxPoll();
      setCaptchaOpen(false);
      return;
    }
    setError("");
  }, [isOpen, stopWxPoll]);

  useEffect(() => {
    return () => stopWxPoll();
  }, [stopWxPoll]);

  useEffect(() => {
    if (countdown <= 0) return;
    const timer = window.setTimeout(() => setCountdown((value) => value - 1), 1000);
    return () => window.clearTimeout(timer);
  }, [countdown]);

  const loadWxQrCode = useCallback(async (): Promise<void> => {
    stopWxPoll();
    setWxQrError("");
    setWxQrLoading(true);
    setWxSceneCode("");
    setWxQrUrl("");
    setWxNeedBindPhone(false);
    setWxRegisterToken("");

    try {
      const payload = await readJsonResponse(
        await fetch("/api/auth/wxgh-qrcode", {
          method: "GET",
          headers: { Accept: "application/json" },
        }),
        t("auth.wechatQrLoadFailed")
      );
      const data = payload.data;
      if (!isRecord(data)) {
        throw new Error(t("auth.wechatQrLoadFailed"));
      }

      const scene = data.sceneCode ?? data.scene;
      const qrUrl = data.qrCodeUrl ?? data.qrUrl ?? data.url ?? data.qr_code;
      if (typeof scene !== "string" || !scene.trim() || typeof qrUrl !== "string" || !qrUrl.trim()) {
        throw new Error(t("auth.wechatQrLoadFailed"));
      }

      setWxSceneCode(scene.trim());
      setWxQrUrl(qrUrl.trim());
    } catch (nextError) {
      setWxQrError(nextError instanceof Error ? nextError.message : t("auth.wechatQrLoadFailed"));
    } finally {
      setWxQrLoading(false);
    }
  }, [stopWxPoll, t]);

  useEffect(() => {
    if (!isOpen || mode !== "wechat") {
      clearWxState();
      return;
    }

    void loadWxQrCode();
  }, [clearWxState, isOpen, loadWxQrCode, mode]);

  useEffect(() => {
    if (!isOpen || mode !== "wechat" || !wxSceneCode || wxNeedBindPhone) {
      stopWxPoll();
      return;
    }

    let cancelled = false;

    const tick = async (): Promise<void> => {
      if (cancelled) return;
      try {
        const params = new URLSearchParams({ sceneCode: wxSceneCode });
        const payload = await readJsonResponse(
          await fetch(`/api/auth/check-wxgh-login?${params.toString()}`, {
            method: "GET",
            headers: { Accept: "application/json" },
          }),
          t("auth.wechatPollFailed")
        );
        if (cancelled) return;

        const data = payload.data;
        if (!isRecord(data)) {
          wxPollTimerRef.current = window.setTimeout(() => void tick(), 2000);
          return;
        }

        if (data.needBindPhone === true) {
          const registerToken = typeof data.registerToken === "string" ? data.registerToken.trim() : "";
          if (!registerToken) {
            throw new Error(t("auth.wechatMissingBindToken"));
          }
          stopWxPoll();
          setWxRegisterToken(registerToken);
          setWxNeedBindPhone(true);
          return;
        }

        const token = typeof data.token === "string" ? data.token.trim() : "";
        if (token) {
          stopWxPoll();
          await completeLoginWithToken(token);
          return;
        }

        wxPollTimerRef.current = window.setTimeout(() => void tick(), 2000);
      } catch (nextError) {
        stopWxPoll();
        if (!cancelled) {
          setWxQrError(nextError instanceof Error ? nextError.message : t("auth.wechatPollFailed"));
        }
      }
    };

    stopWxPoll();
    void tick();

    return () => {
      cancelled = true;
      stopWxPoll();
    };
  }, [completeLoginWithToken, isOpen, mode, stopWxPoll, t, wxNeedBindPhone, wxSceneCode]);

  const loadCaptcha = async (): Promise<void> => {
    setCaptchaError("");
    const response = await fetch("/api/captcha/gen", {
      method: "GET",
      headers: { Accept: "application/json" },
    });
    const payload = await readJsonResponse(response, t("auth.captchaLoadFailed"));
    if (!isRecord(payload.data)) {
      throw new Error(t("auth.captchaLoadFailed"));
    }

    const nextCaptchaId = payload.data.id;
    const nextCaptchaImage = payload.data.data;
    if (typeof nextCaptchaId !== "string" || typeof nextCaptchaImage !== "string") {
      throw new Error(t("auth.captchaLoadFailed"));
    }

    setCaptchaId(nextCaptchaId);
    setCaptchaImage(nextCaptchaImage);
    setCaptchaValue("");
  };

  const handleSendCode = async (targetPhone: string, purpose: CodePurpose): Promise<void> => {
    if (!targetPhone.trim() || countdown > 0 || captchaLoading || isSendingCode) return;

    setError("");
    setCaptchaError("");
    setCodeTargetPhone(targetPhone.trim());
    setCodePurpose(purpose);
    setCaptchaOpen(true);
    try {
      await loadCaptcha();
    } catch (nextError) {
      setCaptchaError(nextError instanceof Error ? nextError.message : t("auth.captchaLoadFailed"));
    }
  };

  const handleCaptchaOk = async (): Promise<void> => {
    const trimmedCaptchaValue = captchaValue.trim();
    if (!captchaId || !trimmedCaptchaValue || captchaLoading || !codeTargetPhone) return;

    setCaptchaLoading(true);
    setCaptchaError("");
    try {
      const verifyResponse = await fetch("/api/captcha/verify", {
        method: "POST",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ id: captchaId, value: trimmedCaptchaValue }),
      });
      const verifyPayload = await readJsonResponse(verifyResponse, t("auth.captchaInvalid"));
      const verifyData = verifyPayload.data;
      if (isRecord(verifyData) && verifyData.err !== 0) {
        throw new Error(getMessage(verifyPayload, t("auth.captchaInvalid")));
      }

      setIsSendingCode(true);
      const params = new URLSearchParams({
        phone: codeTargetPhone,
        usage: "LOGIN",
        captchaId,
        captchaValue: trimmedCaptchaValue,
      });
      await readJsonResponse(
        await fetch(`/api/auth/code?${params.toString()}`, {
          method: "GET",
          headers: { Accept: "application/json" },
        }),
        t("auth.captchaSendFailed")
      );

      setCountdown(60);
      setCaptchaOpen(false);
      setCaptchaId("");
      setCaptchaImage("");
      setCaptchaValue("");
      if (codePurpose === "wechat-bind") {
        setWxBindCode("");
      }
    } catch (nextError) {
      setCaptchaError(nextError instanceof Error ? nextError.message : t("auth.captchaSendFailed"));
      void loadCaptcha().catch(() => undefined);
    } finally {
      setCaptchaLoading(false);
      setIsSendingCode(false);
    }
  };

  const handleLogin = async (): Promise<void> => {
    if (isLoading || mode === "wechat") return;
    setError("");
    setIsLoading(true);

    try {
      const endpoint = mode === "password" ? "/api/auth/login" : "/api/auth/login-by-code";
      const body =
        mode === "password"
          ? { username: phone.trim(), password }
          : { phone: phone.trim(), code: code.trim(), inviteCode: inviteCode.trim() || undefined };

      const payload = await readJsonResponse(
        await fetch(endpoint, {
          method: "POST",
          headers: {
            Accept: "application/json",
            "Content-Type": "application/json",
          },
          body: JSON.stringify(body),
        }),
        t("auth.loginFailed")
      );

      const token = getTokenFromResponse(payload);
      if (!token) {
        throw new Error(t("auth.loginMissingToken"));
      }
      await completeLoginWithToken(token);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : t("auth.loginFailed"));
    } finally {
      setIsLoading(false);
    }
  };

  const handleWxBindPhone = async (): Promise<void> => {
    if (isLoading || !wxRegisterToken || !wxBindPhone.trim() || !wxBindCode.trim()) return;

    setError("");
    setIsLoading(true);
    try {
      const payload = await readJsonResponse(
        await fetch("/api/auth/wxgh-register-by-phone", {
          method: "POST",
          headers: {
            Accept: "application/json",
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            registerToken: wxRegisterToken,
            phone: wxBindPhone.trim(),
            code: wxBindCode.trim(),
            inviteCode: wxBindInviteCode.trim() || undefined,
          }),
        }),
        t("auth.bindPhoneFailed")
      );

      const token = getTokenFromResponse(payload);
      if (!token) {
        throw new Error(t("auth.loginMissingToken"));
      }
      await completeLoginWithToken(token);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : t("auth.bindPhoneFailed"));
    } finally {
      setIsLoading(false);
    }
  };

  const isSubmitDisabled =
    isLoading ||
    mode === "wechat" ||
    !phone.trim() ||
    (mode === "password" ? !password : !code.trim()) ||
    !agreementChecked;

  const isWxBindSubmitDisabled =
    isLoading || !wxRegisterToken || !wxBindPhone.trim() || !wxBindCode.trim();

  return (
    <>
      <Modal
        open={isOpen}
        title={reason === "unauthorized" ? t("auth.loginExpired") : t("auth.loginTitle")}
        onCancel={() => {
          stopWxPoll();
          closeLoginModal();
        }}
        footer={null}
        centered
        width={420}
        destroyOnHidden
        mask={{ closable: false }}
      >
        <div className="flex flex-col gap-4 pt-1">
          {reason === "unauthorized" ? (
            <Alert type="warning" showIcon title={t("auth.reloginRequired")} />
          ) : null}
          {error ? <Alert type="error" showIcon title={error} /> : null}

          <Segmented
            block
            value={mode}
            onChange={(value) => {
              setMode(value as LoginMode);
              setError("");
            }}
            options={[
              { label: t("auth.codeLogin"), value: "code" },
              { label: t("auth.wechatLogin"), value: "wechat" },
              { label: t("auth.passwordLogin"), value: "password" },
            ]}
          />

          {mode === "wechat" ? (
            wxNeedBindPhone ? (
              <>
                <Input
                  value={wxBindPhone}
                  onChange={(event) => setWxBindPhone(event.target.value)}
                  placeholder={t("auth.phonePlaceholder")}
                  autoComplete="tel"
                />
                <div className="flex gap-2">
                  <Input
                    value={wxBindCode}
                    onChange={(event) => setWxBindCode(event.target.value)}
                    placeholder={t("auth.smsCodePlaceholder")}
                    autoComplete="one-time-code"
                    onPressEnter={() => {
                      if (!isWxBindSubmitDisabled) void handleWxBindPhone();
                    }}
                  />
                  <Button
                    className="shrink-0"
                    onClick={() => void handleSendCode(wxBindPhone, "wechat-bind")}
                    loading={isSendingCode}
                    disabled={!wxBindPhone.trim() || countdown > 0}
                  >
                    {countdown > 0 ? `${countdown}s` : t("auth.sendCode")}
                  </Button>
                </div>
                <Input
                  value={wxBindInviteCode}
                  onChange={(event) => setWxBindInviteCode(event.target.value)}
                  placeholder={t("auth.inviteCodePlaceholder")}
                />
                <Button
                  type="primary"
                  size="large"
                  block
                  onClick={() => void handleWxBindPhone()}
                  loading={isLoading}
                  disabled={isWxBindSubmitDisabled}
                >
                  {t("auth.bindPhoneConfirm")}
                </Button>
              </>
            ) : (
              <div className="flex flex-col items-center gap-3">
                {wxQrError ? <Alert type="error" showIcon title={wxQrError} className="w-full" /> : null}
                {wxQrUrl ? (
                  <img
                    src={wxQrUrl}
                    alt={t("auth.wechatScanAlt")}
                    className="h-48 w-48 rounded-lg border border-neutral-200 bg-white object-contain p-2"
                  />
                ) : (
                  <div className="flex h-48 w-48 items-center justify-center rounded-lg border border-neutral-200 bg-neutral-50 text-sm text-neutral-500">
                    {wxQrLoading ? t("auth.loading") : t("auth.wechatNoQr")}
                  </div>
                )}
                <div className="text-sm text-neutral-500">{t("auth.wechatScanTip")}</div>
                <Button onClick={() => void loadWxQrCode()} loading={wxQrLoading}>
                  {t("auth.wechatReloadQr")}
                </Button>
                <p className="text-center text-xs leading-5 text-neutral-500">
                  登录即代表同意
                  <a
                    href="https://www.popi.art/content/userTerms"
                    target="_blank"
                    rel="noreferrer"
                    className="text-blue-500 hover:underline"
                  >
                    《用户协议》
                  </a>
                  和
                  <a
                    href="https://www.popi.art/content/privacyPolicy"
                    target="_blank"
                    rel="noreferrer"
                    className="text-blue-500 hover:underline"
                  >
                    《隐私政策》
                  </a>
                </p>
              </div>
            )
          ) : (
            <>
              <Input
                value={phone}
                onChange={(event) => setPhone(event.target.value)}
                placeholder={t("auth.phonePlaceholder")}
                autoComplete="tel"
              />

              {mode === "password" ? (
                <Input.Password
                  value={password}
                  onChange={(event) => setPassword(event.target.value)}
                  placeholder={t("auth.passwordPlaceholder")}
                  autoComplete="current-password"
                  onPressEnter={() => {
                    if (!isSubmitDisabled) void handleLogin();
                  }}
                />
              ) : (
                <>
                  <div className="flex gap-2">
                    <Input
                      value={code}
                      onChange={(event) => setCode(event.target.value)}
                      placeholder={t("auth.smsCodePlaceholder")}
                      autoComplete="one-time-code"
                      onPressEnter={() => {
                        if (!isSubmitDisabled) void handleLogin();
                      }}
                    />
                    <Button
                      className="shrink-0"
                      onClick={() => void handleSendCode(phone, "login")}
                      loading={isSendingCode}
                      disabled={!phone.trim() || countdown > 0}
                    >
                      {countdown > 0 ? `${countdown}s` : t("auth.sendCode")}
                    </Button>
                  </div>

                  <Input
                    value={inviteCode}
                    onChange={(event) => setInviteCode(event.target.value)}
                    placeholder={t("auth.inviteCodePlaceholder")}
                  />
                </>
              )}

              <Button
                type="primary"
                size="large"
                block
                onClick={() => void handleLogin()}
                loading={isLoading}
                disabled={isSubmitDisabled}
              >
                {mode === "password" ? t("auth.login") : t("auth.loginOrRegister")}
              </Button>
              <Checkbox
                checked={agreementChecked}
                onChange={(event) => setAgreementChecked(event.target.checked)}
                className="text-xs leading-5 text-neutral-500"
              >
                登录即代表同意
                <a
                  href="https://www.popi.art/content/userTerms"
                  target="_blank"
                  rel="noreferrer"
                  className="text-blue-500 hover:underline"
                >
                  《用户协议》
                </a>
                和
                <a
                  href="https://www.popi.art/content/privacyPolicy"
                  target="_blank"
                  rel="noreferrer"
                  className="text-blue-500 hover:underline"
                >
                  《隐私政策》
                </a>
                ，未注册手机号将自动注册
              </Checkbox>
            </>
          )}
        </div>
      </Modal>

      <Modal
        open={captchaOpen}
        title={t("auth.captchaTitle")}
        onCancel={() => setCaptchaOpen(false)}
        footer={null}
        centered
        width={360}
        destroyOnHidden
        mask={{ closable: true }}
      >
        <div className="flex flex-col gap-4">
          {captchaError ? <Alert type="error" showIcon title={captchaError} /> : null}
          <div className="flex items-center justify-center">
            {captchaImage ? (
              <img
                src={captchaImage}
                alt={t("auth.captchaAlt")}
                className="h-[72px] cursor-pointer rounded border border-neutral-200 bg-white"
                onClick={() => void loadCaptcha()}
              />
            ) : (
              <div className="flex h-[72px] w-[180px] items-center justify-center rounded border border-neutral-200 bg-neutral-50 text-sm text-neutral-500">
                {t("auth.loading")}
              </div>
            )}
          </div>
          <Button size="small" onClick={() => void loadCaptcha()}>
            {t("auth.captchaRefresh")}
          </Button>
          <Input
            value={captchaValue}
            onChange={(event) => setCaptchaValue(event.target.value)}
            placeholder={t("auth.captchaInputPlaceholder")}
            onPressEnter={() => void handleCaptchaOk()}
          />
          <Button
            type="primary"
            block
            loading={captchaLoading || isSendingCode}
            disabled={!captchaValue.trim() || !captchaId}
            onClick={() => void handleCaptchaOk()}
          >
            {t("auth.confirm")}
          </Button>
        </div>
      </Modal>
    </>
  );
}
