import { writable, get } from "svelte/store";
import {
  readReferralParam,
  clearAuthQuery,
  buildTelegramOAuthStartUrl,
  emailError,
} from "../authHelpers.js";

const EMAIL_CODE_PENDING_STORAGE_KEY = "rw_email_code_login_pending_v1";
const EMAIL_CODE_PENDING_TTL_MS = 10 * 60 * 1000;
const EMAIL_CODE_RESEND_MS = 60 * 1000;

export function createAuthStore({
  publicApi,
  setToken,
  loadData,
  telegramSdk,
  getTg,
  t,
  currentLang,
}) {
  const state = writable({
    authStatus: "",
    authIsError: false,
    authBusy: false,
    telegramLoginBusy: false,
    telegramLoginAttemptId: 0,
    loginEmailFieldError: "",
    loginEmailTooltipOpen: false,
    authResendCooldown: 0,
    email: "",
    emailPassword: "",
    pendingEmail: "",
    emailCode: "",
    passwordLoginMode: false,
    passwordLoginFallback: false,
  });

  let authResendTimer = null;
  let telegramLoginWatchdogTimer = null;

  function readPendingEmailCodeSession() {
    if (typeof window === "undefined" || !window.sessionStorage) return null;
    try {
      const raw = window.sessionStorage.getItem(EMAIL_CODE_PENDING_STORAGE_KEY);
      if (!raw) return null;
      const parsed = JSON.parse(raw);
      const email = String(parsed?.email || "")
        .trim()
        .toLowerCase();
      const expiresAt = Number(parsed?.expiresAt || 0);
      const cooldownUntil = Number(parsed?.cooldownUntil || 0);
      if (!email || !email.includes("@") || !expiresAt || expiresAt <= Date.now()) {
        window.sessionStorage.removeItem(EMAIL_CODE_PENDING_STORAGE_KEY);
        return null;
      }
      return { email, expiresAt, cooldownUntil };
    } catch (_error) {
      window.sessionStorage.removeItem(EMAIL_CODE_PENDING_STORAGE_KEY);
      return null;
    }
  }

  function writePendingEmailCodeSession(email) {
    if (typeof window === "undefined" || !window.sessionStorage) return;
    try {
      window.sessionStorage.setItem(
        EMAIL_CODE_PENDING_STORAGE_KEY,
        JSON.stringify({
          email,
          expiresAt: Date.now() + EMAIL_CODE_PENDING_TTL_MS,
          cooldownUntil: Date.now() + EMAIL_CODE_RESEND_MS,
        })
      );
    } catch (_error) {
      void _error;
    }
  }

  function clearPendingEmailCode() {
    if (typeof window === "undefined" || !window.sessionStorage) return;
    try {
      window.sessionStorage.removeItem(EMAIL_CODE_PENDING_STORAGE_KEY);
    } catch (_error) {
      void _error;
    }
  }

  function restorePendingEmailCode(changeScreen) {
    const pending = readPendingEmailCodeSession();
    if (!pending) return false;
    state.update((s) => ({
      ...s,
      email: pending.email,
      pendingEmail: pending.email,
      emailCode: "",
      authStatus: "",
      authIsError: false,
      authBusy: false,
      passwordLoginMode: false,
      passwordLoginFallback: false,
      loginEmailFieldError: "",
      loginEmailTooltipOpen: false,
    }));
    const cooldownSeconds = Math.ceil((pending.cooldownUntil - Date.now()) / 1000);
    if (cooldownSeconds > 0) {
      startCooldownTimer(cooldownSeconds);
    } else {
      clearCooldownTimer();
      state.update((s) => ({ ...s, authResendCooldown: 0 }));
    }
    if (typeof changeScreen === "function") changeScreen("code");
    return true;
  }

  function setAuthStatus(message, isError = false) {
    state.update((s) => ({ ...s, authStatus: message, authIsError: isError }));
  }

  function clearCooldownTimer() {
    if (authResendTimer) {
      window.clearInterval(authResendTimer);
      authResendTimer = null;
    }
  }

  function startCooldownTimer(seconds = 60) {
    clearCooldownTimer();
    state.update((s) => ({ ...s, authResendCooldown: Math.max(0, Number(seconds || 60)) }));
    authResendTimer = window.setInterval(() => {
      const { authResendCooldown } = get(state);
      if (authResendCooldown <= 1) {
        state.update((s) => ({ ...s, authResendCooldown: 0 }));
        clearCooldownTimer();
        return;
      }
      state.update((s) => ({ ...s, authResendCooldown: authResendCooldown - 1 }));
    }, 1000);
  }

  function startTelegramLoginWatchdog() {
    const TELEGRAM_MINI_APP_AUTH_TIMEOUT_MS = 6000;
    stopTelegramLoginWatchdog();
    state.update((s) => ({ ...s, telegramLoginAttemptId: s.telegramLoginAttemptId + 1 }));
    const { telegramLoginAttemptId } = get(state);

    telegramLoginWatchdogTimer = window.setTimeout(() => {
      if (get(state).telegramLoginAttemptId !== telegramLoginAttemptId) return;
      telegramLoginWatchdogTimer = null;
      state.update((s) => ({ ...s, telegramLoginBusy: false, authBusy: false }));
      setAuthStatus(t("wa_auth_telegram_timeout"), true);
    }, TELEGRAM_MINI_APP_AUTH_TIMEOUT_MS);

    return telegramLoginAttemptId;
  }

  function stopTelegramLoginWatchdog(attemptId = null) {
    if (attemptId !== null && attemptId !== get(state).telegramLoginAttemptId) return;
    if (telegramLoginWatchdogTimer) {
      window.clearTimeout(telegramLoginWatchdogTimer);
      telegramLoginWatchdogTimer = null;
    }
  }

  function isActiveTelegramLoginAttempt(attemptId) {
    const s = get(state);
    return attemptId === s.telegramLoginAttemptId && s.telegramLoginBusy;
  }

  async function finalizeMagicLogin(loginToken) {
    const s = get(state);
    if (s.authBusy) return false;
    state.update((s) => ({ ...s, authBusy: true }));
    setAuthStatus(t("wa_auth_checking_login"));
    try {
      const payload = { token: loginToken };
      const referralParam = readReferralParam(getTg());
      if (referralParam) payload.referral_code = referralParam;
      const response = await publicApi("/auth/email/magic", payload);
      if (response.ok && response.csrf_token) {
        setToken("", response.csrf_token);
        clearPendingEmailCode();
        clearAuthQuery();
        await loadData();
        return true;
      }
      setAuthStatus(t("wa_auth_login_confirm_failed"), true);
    } catch {
      setAuthStatus(t("wa_auth_login_confirm_failed"), true);
    } finally {
      state.update((s) => ({ ...s, authBusy: false }));
    }
    return false;
  }

  async function finalizeTelegramAuth(authData, source = "auth_data", options = {}) {
    const s = get(state);
    if (s.authBusy) return false;
    state.update((s) => ({ ...s, authBusy: true }));
    setAuthStatus(t("wa_auth_checking_telegram"));
    try {
      const payload =
        source === "init_data"
          ? { init_data: authData }
          : source === "id_token"
            ? { id_token: authData.id_token, nonce: authData.nonce }
            : { auth_data: authData };
      const referralParam = readReferralParam(getTg());
      if (referralParam) payload.referral_code = referralParam;
      const response = await publicApi("/auth/token", payload, { signal: options.signal });
      if (response.ok && response.csrf_token) {
        setToken("", response.csrf_token);
        clearPendingEmailCode();
        clearAuthQuery();
        setAuthStatus("");
        await loadData();
        return true;
      }
      setAuthStatus(
        response.error === "banned"
          ? t("wa_auth_access_denied")
          : t("wa_auth_telegram_not_confirmed"),
        true
      );
    } catch (error) {
      setAuthStatus(
        error?.name === "AbortError"
          ? t("wa_auth_telegram_timeout")
          : t("wa_auth_telegram_unavailable"),
        true
      );
    } finally {
      state.update((s) => ({ ...s, authBusy: false }));
    }
    return false;
  }

  async function requestEmailCode(changeScreen) {
    const s = get(state);
    const normalized = s.email.trim().toLowerCase();
    if (
      s.authResendCooldown > 0 &&
      s.pendingEmail &&
      (!normalized || normalized === s.pendingEmail)
    ) {
      if (typeof changeScreen === "function") changeScreen("code");
      return;
    }
    if (!normalized || !normalized.includes("@")) {
      state.update((s) => ({
        ...s,
        loginEmailFieldError: t("wa_auth_invalid_email"),
        loginEmailTooltipOpen: true,
      }));
      return;
    }
    state.update((s) => ({
      ...s,
      loginEmailFieldError: "",
      loginEmailTooltipOpen: false,
      authBusy: true,
      passwordLoginFallback: false,
    }));
    setAuthStatus(t("wa_auth_sending_code"));
    try {
      const payload = { email: normalized, language: currentLang() };
      const referralParam = readReferralParam(getTg());
      if (referralParam) payload.referral_code = referralParam;
      const response = await publicApi("/auth/email/request", payload);
      if (!response.ok) throw response;
      const presetCode = String(response.email_code || response.code || "")
        .replace(/\D/g, "")
        .slice(0, 6);
      state.update((s) => ({ ...s, pendingEmail: normalized, emailCode: presetCode }));
      writePendingEmailCodeSession(normalized);
      changeScreen("code");
      setAuthStatus("");
      startCooldownTimer(60);
    } catch (error) {
      setAuthStatus(emailError(error, t("wa_auth_send_code_failed"), t), true);
    } finally {
      state.update((s) => ({ ...s, authBusy: false }));
    }
  }

  async function loginWithEmailPassword() {
    const s = get(state);
    const normalized = s.email.trim().toLowerCase();
    const password = String(s.emailPassword || "");
    if (!normalized || !normalized.includes("@")) {
      state.update((s) => ({
        ...s,
        loginEmailFieldError: t("wa_auth_invalid_email"),
        loginEmailTooltipOpen: true,
      }));
      return;
    }
    if (!password) {
      setAuthStatus(t("wa_auth_password_required"), true);
      return;
    }
    state.update((s) => ({
      ...s,
      loginEmailFieldError: "",
      loginEmailTooltipOpen: false,
      authBusy: true,
      passwordLoginFallback: false,
    }));
    setAuthStatus(t("wa_auth_checking_password"));
    try {
      const response = await publicApi("/auth/email/password", {
        email: normalized,
        password,
      });
      if (!response.ok || !response.csrf_token) throw response;
      setToken("", response.csrf_token);
      clearPendingEmailCode();
      await loadData();
      setAuthStatus("");
    } catch (error) {
      if (error?.error === "rate_limited") {
        setAuthStatus(emailError(error, t("wa_auth_password_login_failed"), t), true);
      } else if (error?.error === "banned") {
        setAuthStatus(t("wa_auth_access_denied"), true);
      } else {
        state.update((s) => ({ ...s, passwordLoginFallback: true }));
        setAuthStatus(t("wa_auth_password_login_failed"), true);
      }
    } finally {
      state.update((s) => ({ ...s, authBusy: false }));
    }
  }

  async function verifyEmailCode() {
    const s = get(state);
    const code = s.emailCode.replace(/\\D/g, "").slice(0, 6);
    if (code.length !== 6) {
      setAuthStatus(t("wa_auth_enter_code_6digits"), true);
      return;
    }
    state.update((s) => ({ ...s, authBusy: true }));
    setAuthStatus(t("wa_auth_checking_code"));
    try {
      const payload = { email: s.pendingEmail, code };
      const referralParam = readReferralParam(getTg());
      if (referralParam) payload.referral_code = referralParam;
      const response = await publicApi("/auth/email/verify", payload);
      if (!response.ok || !response.csrf_token) throw response;
      setToken("", response.csrf_token);
      clearPendingEmailCode();
      await loadData();
      setAuthStatus("");
    } catch (error) {
      setAuthStatus(emailError(error, t("wa_auth_invalid_code"), t), true);
    } finally {
      state.update((s) => ({ ...s, authBusy: false }));
    }
  }

  async function openTelegramLogin(telegramOAuthClientId, getTelegramMiniAppInitData) {
    const s = get(state);
    if (s.authBusy || s.telegramLoginBusy) return;
    setAuthStatus("");

    const isTelegramMiniAppAttempt = telegramSdk.hasLaunchParams();
    if (!isTelegramMiniAppAttempt && telegramOAuthClientId) {
      state.update((s) => ({ ...s, telegramLoginBusy: true }));
      window.location.assign(buildTelegramOAuthStartUrl("login", getTg()));
      window.setTimeout(() => {
        state.update((s) => ({ ...s, telegramLoginBusy: false }));
      }, 1500);
      return;
    }

    state.update((s) => ({ ...s, telegramLoginBusy: true }));
    const attemptId = startTelegramLoginWatchdog();
    const loginTimeout = telegramSdk.createMiniAppAuthTimeout();
    try {
      await Promise.race([
        (async () => {
          await telegramSdk.ensureForAction();
          if (!isActiveTelegramLoginAttempt(attemptId)) return;
          const initData = getTelegramMiniAppInitData();
          if (initData) {
            await finalizeTelegramAuth(initData, "init_data", { signal: loginTimeout.signal });
            return;
          }

          if (!telegramOAuthClientId) {
            setAuthStatus(t("wa_auth_telegram_not_configured"), true);
            return;
          }

          window.location.assign(buildTelegramOAuthStartUrl("login", getTg()));
        })(),
        loginTimeout.promise,
      ]);
    } catch (error) {
      if (!isActiveTelegramLoginAttempt(attemptId)) return;
      if (error?.name === "AbortError") {
        setAuthStatus(t("wa_auth_telegram_timeout"), true);
      } else {
        setAuthStatus(t("wa_auth_telegram_unavailable"), true);
      }
    } finally {
      loginTimeout.clear();
      if (loginTimeout.timedOut) {
        setAuthStatus(t("wa_auth_telegram_timeout"), true);
        state.update((s) => ({ ...s, authBusy: false }));
      }
      if (isActiveTelegramLoginAttempt(attemptId)) {
        stopTelegramLoginWatchdog(attemptId);
        state.update((s) => ({ ...s, telegramLoginBusy: false }));
      }
    }
  }

  return {
    subscribe: state.subscribe,
    set: state.set,
    update: state.update,
    finalizeMagicLogin,
    finalizeTelegramAuth,
    requestEmailCode,
    loginWithEmailPassword,
    verifyEmailCode,
    openTelegramLogin,
    restorePendingEmailCode,
    clearPendingEmailCode,
    clearCooldownTimer,
    stopTelegramLoginWatchdog,
    setAuthStatus,
  };
}
