/* ============ AUTH MODULE — window.AUTH + AuthWall component ============ */
/* global React */

const AUTH_TOKEN_KEY = "__pz_auth_token";
const AUTH_USER_KEY = "__pz_auth_user";
const AUTH_PREFS_KEY = "__pz_auth_prefs";

const AUTH_LISTENERS = new Set();
function notifyAuth() {
  AUTH_LISTENERS.forEach((cb) => {
    try {
      cb(window.AUTH.user);
    } catch {}
  });
}

function loadStoredUser() {
  try {
    return JSON.parse(localStorage.getItem(AUTH_USER_KEY) || "null");
  } catch {
    return null;
  }
}
function loadStoredToken() {
  return localStorage.getItem(AUTH_TOKEN_KEY) || null;
}
function loadStoredPrefs() {
  try {
    return JSON.parse(localStorage.getItem(AUTH_PREFS_KEY) || "{}") || {};
  } catch {
    return {};
  }
}

function setSession(token, user, prefs, plan) {
  if (token) localStorage.setItem(AUTH_TOKEN_KEY, token);
  if (user) localStorage.setItem(AUTH_USER_KEY, JSON.stringify(user));
  if (prefs && typeof prefs === "object") {
    localStorage.setItem(AUTH_PREFS_KEY, JSON.stringify(prefs));
    window.AUTH.prefs = prefs;
  }
  window.AUTH.token = token || null;
  window.AUTH.user = user || null;
  // Backward compat z istniejącym kodem PRO
  try {
    if (user) localStorage.setItem("proUser", JSON.stringify(user));
    else localStorage.removeItem("proUser");
    // Plan z serwera nadpisuje localStorage (źródło prawdy = backend)
    if (plan !== undefined) {
      window.AUTH.plan = plan || null;
      if (plan && plan.paid)
        localStorage.setItem("proPlan", JSON.stringify(plan));
      else localStorage.removeItem("proPlan");
    }
  } catch {}
  notifyAuth();
}

function clearSession() {
  localStorage.removeItem(AUTH_TOKEN_KEY);
  localStorage.removeItem(AUTH_USER_KEY);
  localStorage.removeItem(AUTH_PREFS_KEY);
  try {
    localStorage.removeItem("proUser");
  } catch {}
  window.AUTH.token = null;
  window.AUTH.user = null;
  window.AUTH.prefs = {};
  notifyAuth();
}

async function apiPost(action, body) {
  const res = await fetch("/api/auth", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ action, ...body }),
  });
  let data = null;
  try {
    data = await res.json();
  } catch {}
  return { res, data: data || {} };
}

async function register(email, password, passwordConfirm) {
  return apiPost("register", { email, password, passwordConfirm });
}
async function login(email, password) {
  const { res, data } = await apiPost("login", { email, password });
  if (res.ok && data.token) {
    setSession(data.token, data.user, data.prefs || {}, data.plan || null);
  }
  return { res, data };
}
async function refresh() {
  const tok = loadStoredToken();
  if (!tok) return null;
  try {
    const { res, data } = await apiPost("me", { token: tok });
    if (res.ok && data.ok) {
      // Jeśli backend zwraca plan=null, ale lokalny cache pokazuje paid plan
      // dla tego usera — NIE nadpisuj go null-em (race: setPlan właśnie leciał,
      // backend jeszcze nie zdążył; albo chwilowy 5xx zgubił sync). Wciąż zachowaj
      // cache, retry sync w tle.
      let resolvedPlan = data.plan || null;
      if (!resolvedPlan) {
        try {
          const cached = JSON.parse(localStorage.getItem("proPlan") || "null");
          const cacheStillValid =
            cached?.paid &&
            (cached.lifetime ||
              (cached.expiresAt && cached.expiresAt > Date.now()));
          if (cacheStillValid) {
            resolvedPlan = cached;
            // Spróbuj ponownie zsyncować lokalny plan na backend
            setTimeout(() => {
              if (window.AUTH?.setPlan)
                window.AUTH.setPlan(cached).catch(() => {});
            }, 1000);
          }
        } catch {}
      }
      setSession(tok, data.user, data.prefs || {}, resolvedPlan);
      return data.user;
    }
    if (res.status === 401) clearSession();
  } catch {
    // Network blip — NIE clearSession, żeby user nie widział "wylogowane" przy chwilowym fail.
    return null;
  }
  return null;
}
async function setPlan(plan) {
  const tok = loadStoredToken();
  if (!tok) return { ok: false, error: "not_logged_in" };
  // Optymistycznie ustaw lokalnie ZARAZ — żeby AI LAB i UI od razu widziały nowy plan,
  // niezależnie od opóźnienia backendu / chwilowego błędu sieci.
  if (plan && typeof plan === "object") {
    window.AUTH.plan = plan;
    try {
      localStorage.setItem("proPlan", JSON.stringify(plan));
    } catch {}
    notifyAuth();
  }
  return { ok: true };
}

// =====================================================================
// SINGLE SOURCE OF TRUTH dla aktualnego tieru.
// Używane przez AI LAB i wszelkie inne UI gdzie liczy się plan.
// Logika z fallbackami:
//   1. Brak user-a (gość) → free.
//   2. window.AUTH.plan z backendu (preferowane).
//   3. Fallback: localStorage proPlan jeśli AUTH.plan null/ unpaid (race conditions:
//      user właśnie zapłacił, backend jeszcze nie zsyncował, ale my mamy cache).
//   4. Walidacja: paid && (lifetime || expiresAt > now).
//   5. Map tier: 'epic' → epic, reszta paid → pro, brak → free.
// =====================================================================
function getActiveTier() {
  if (!window.AUTH?.user)
    return { tier: "free", plan: null, reason: "no_user" };
  let plan = window.AUTH.plan;
  if (!plan || !plan.paid) {
    try {
      const cached = JSON.parse(localStorage.getItem("proPlan") || "null");
      if (cached && cached.paid) plan = cached;
    } catch {}
  }
  if (!plan || !plan.paid)
    return { tier: "free", plan: null, reason: "no_paid_plan" };
  // "Valid" jeśli:
  //  - lifetime (bez expiresAt)
  //  - expiresAt > now
  //  - subskrypcja active/trialing u Stripe (mimo że expiresAt może być chwilowo null
  //    przy świeżej subskrypcji — Stripe wypełnia current_period_end z opóźnieniem)
  const now = Date.now();
  const isStripeActive =
    plan.stripe_status === "active" || plan.stripe_status === "trialing";
  const periodValid = !!plan.expiresAt && plan.expiresAt > now;
  const expired =
    !plan.lifetime && !isStripeActive && plan.expiresAt && !periodValid;
  if (expired) return { tier: "free", plan, reason: "plan_expired" };
  // Wszystkie inne ścieżki z paid:true → uznajemy aktywne
  const t = (plan.tier || "pro").toLowerCase();
  return {
    tier: t === "epic" ? "epic" : "pro",
    plan,
    reason: "active",
  };
}

// Debounced save prefs do backendu (i lokalnego cache)
let _prefsSaveTimer = null;
let _prefsPending = {};
async function flushPrefsSave() {
  const tok = loadStoredToken();
  const partial = _prefsPending;
  _prefsPending = {};
  if (!tok || Object.keys(partial).length === 0) return;
  try {
    const { res, data } = await apiPost("set-prefs", {
      token: tok,
      prefs: partial,
    });
    if (res.ok && data.prefs) {
      localStorage.setItem(AUTH_PREFS_KEY, JSON.stringify(data.prefs));
      window.AUTH.prefs = data.prefs;
    }
  } catch {
    // Brak sieci — partial już jest w localStorage z savePrefs(), spróbujemy później
    _prefsPending = { ...partial, ..._prefsPending };
  }
}
function savePrefs(partial) {
  if (!partial || typeof partial !== "object") return;
  // Aktualizacja lokalna natychmiast (optymistyczna)
  const merged = { ...(window.AUTH.prefs || {}), ...partial };
  window.AUTH.prefs = merged;
  try {
    localStorage.setItem(AUTH_PREFS_KEY, JSON.stringify(merged));
  } catch {}
  // Wysyłka do backendu z debounce
  _prefsPending = { ..._prefsPending, ...partial };
  if (_prefsSaveTimer) clearTimeout(_prefsSaveTimer);
  _prefsSaveTimer = setTimeout(flushPrefsSave, 500);
}
async function logout() {
  try {
    await apiPost("logout", {});
  } catch {}
  clearSession();
}
async function resendConfirmation(email) {
  return apiPost("resend", { email });
}

window.AUTH = {
  token: loadStoredToken(),
  user: loadStoredUser(),
  prefs: loadStoredPrefs(),
  plan: (() => {
    try {
      return JSON.parse(localStorage.getItem("proPlan") || "null");
    } catch {
      return null;
    }
  })(),
  register,
  login,
  logout,
  refresh,
  resendConfirmation,
  savePrefs,
  setPlan,
  getActiveTier,
  onChange: (cb) => {
    AUTH_LISTENERS.add(cb);
    return () => AUTH_LISTENERS.delete(cb);
  },
};

// Auto-refresh on load — sprawdź czy token w storage jest jeszcze ważny
(async () => {
  if (window.AUTH.token) {
    await refresh();
  }
})();

// Po returnie z linka potwierdzającego — pokaż banner sukcesu
(function handleConfirmReturn() {
  if (typeof window === "undefined") return;
  const url = new URL(window.location.href);
  const auth = url.searchParams.get("auth");
  if (!auth) return;
  const detail = {
    code: auth,
    email: url.searchParams.get("email") || "",
  };
  // Wyczyść query string żeby przy reloadzie nie pokazywało dwa razy
  url.searchParams.delete("auth");
  url.searchParams.delete("email");
  window.history.replaceState({}, "", url.toString());
  // Pokaż przez bus dopiero gdy będzie gotowy
  const fire = () =>
    window.PrzelomBus && window.PrzelomBus.emit("auth-confirm-result", detail);
  if (window.PrzelomBus) fire();
  else window.addEventListener("DOMContentLoaded", () => setTimeout(fire, 500));
})();

// Po returnie ze Stripe Checkout — wymuś refresh planu z backendu.
// Webhook server-side już powinien był zapisać user.plan w Redis,
// ale dajemy chwilę na propagację i robimy retry.
(function handleStripeReturn() {
  if (typeof window === "undefined") return;
  const url = new URL(window.location.href);
  const stripe = url.searchParams.get("stripe");
  if (!stripe) return;
  const sessionId = url.searchParams.get("session_id") || "";
  url.searchParams.delete("stripe");
  url.searchParams.delete("session_id");
  window.history.replaceState({}, "", url.toString());
  const tryRefresh = async (attempt = 0) => {
    if (!window.AUTH?.refresh) {
      setTimeout(() => tryRefresh(attempt + 1), 800);
      return;
    }
    await window.AUTH.refresh();
    const info = window.AUTH.getActiveTier?.() || { tier: "free" };
    if (info.tier === "free" && attempt < 5 && stripe === "success") {
      setTimeout(() => tryRefresh(attempt + 1), 1500);
    } else {
      const fire = () =>
        window.PrzelomBus &&
        window.PrzelomBus.emit("stripe-return", {
          status: stripe,
          tier: info.tier,
          sessionId,
        });
      if (window.PrzelomBus) fire();
      else
        window.addEventListener("DOMContentLoaded", () =>
          setTimeout(fire, 500),
        );
    }
  };
  setTimeout(tryRefresh, 1200);
})();

// =====================================================================
// ERROR MESSAGES
// =====================================================================
const ERR_PL = {
  invalid_email: "Email ma nieprawidłowy format.",
  weak_password: "Hasło musi mieć min. 8 znaków.",
  password_too_long: "Hasło zbyt długie (max 200 znaków).",
  passwords_dont_match: "Hasła nie pasują do siebie.",
  email_taken: "Ten email jest już zarejestrowany. Zaloguj się.",
  invalid_credentials: "Nieprawidłowy email lub hasło.",
  user_not_found: "Nie znaleziono konta dla tego emaila.",
  wrong_password: "Hasło jest nieprawidłowe.",
  email_not_confirmed:
    "Email nie został jeszcze potwierdzony. Sprawdź skrzynkę.",
  rate_limit: "Za dużo prób. Spróbuj za kilka minut.",
  email_send_failed:
    "Wysyłka maila nie powiodła się. Skontaktuj się z administratorem.",
  storage_unavailable: "Chwilowy problem z bazą danych. Spróbuj ponownie.",
  storage_failed: "Chwilowy problem z bazą danych. Spróbuj ponownie.",
  origin_not_allowed: "Niepoprawny origin requestu.",
  missing_password: "Wpisz hasło.",
  invalid_session: "Sesja wygasła. Zaloguj się ponownie.",
  already_confirmed: "Email już jest potwierdzony.",
};
function errToPl(code) {
  return ERR_PL[code] || "Błąd: " + (code || "nieznany");
}

// =====================================================================
// AUTH WALL — pokazywany zamiast aplikacji gdy user nie zalogowany
// =====================================================================
function AuthWall({ appLabel } = {}) {
  const { useState, useEffect } = React;
  const [view, setView] = useState("login"); // login | register | sent | check
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [password2, setPassword2] = useState("");
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState("");
  const [info, setInfo] = useState("");
  const [devLink, setDevLink] = useState("");

  // Reaguj na komunikaty z URL (po confirmie)
  useEffect(() => {
    if (!window.PrzelomBus) return;
    const off = window.PrzelomBus.on("auth-confirm-result", (d) => {
      if (d.code === "confirmed") {
        setView("login");
        if (d.email) setEmail(d.email);
        setInfo("Email potwierdzony. Możesz się zalogować.");
        setErr("");
      } else if (d.code === "confirm_expired") {
        setErr("Link potwierdzający wygasł. Zarejestruj się ponownie.");
      } else if (d.code) {
        setErr("Potwierdzenie nie powiodło się: " + d.code);
      }
    });
    return () => off?.();
  }, []);

  const onLogin = async (e) => {
    e?.preventDefault?.();
    if (busy) return;
    setBusy(true);
    setErr("");
    setInfo("");
    try {
      const { res, data } = await window.AUTH.login(email, password);
      if (res.ok) {
        setInfo("Zalogowano.");
      } else {
        setErr(errToPl(data.error));
      }
    } catch (e) {
      setErr("Błąd sieci: " + e.message);
    } finally {
      setBusy(false);
    }
  };

  const onRegister = async (e) => {
    e?.preventDefault?.();
    if (busy) return;
    setErr("");
    setInfo("");
    if (password !== password2) {
      setErr("Hasła muszą być identyczne.");
      return;
    }
    if (password.length < 8) {
      setErr("Hasło musi mieć min. 8 znaków.");
      return;
    }
    setBusy(true);
    try {
      const { res, data } = await window.AUTH.register(
        email,
        password,
        password2,
      );
      if (res.ok) {
        setView("sent");
        if (data.devLink) setDevLink(data.devLink);
      } else {
        setErr(errToPl(data.error));
      }
    } catch (e) {
      setErr("Błąd sieci: " + e.message);
    } finally {
      setBusy(false);
    }
  };

  const onResend = async () => {
    if (busy) return;
    setBusy(true);
    setErr("");
    try {
      const { res, data } = await window.AUTH.resendConfirmation(email);
      if (res.ok) {
        setInfo("Wysłano ponownie.");
        if (data.devLink) setDevLink(data.devLink);
      } else {
        setErr(errToPl(data.error));
      }
    } catch (e) {
      setErr("Błąd sieci: " + e.message);
    } finally {
      setBusy(false);
    }
  };

  if (view === "sent") {
    return (
      <div className="auth-wall">
        <div className="auth-card">
          <div className="auth-eyebrow">▣ ZAZA/OS · POTWIERDŹ EMAIL</div>
          <h2 className="auth-title">Sprawdź skrzynkę</h2>
          <p className="auth-text">
            Wysłałem link potwierdzający na <b>{email}</b>. Kliknij go, żeby
            aktywować konto, a potem wróć tutaj i zaloguj się.
          </p>
          <p className="auth-text-dim">
            Nie widzisz maila? Sprawdź spam. Link wygasa po 24h.
          </p>
          {devLink && (
            <div className="auth-dev-link">
              <div className="auth-dev-label">DEV (brak RESEND_API_KEY)</div>
              <a href={devLink} target="_blank" rel="noopener noreferrer">
                {devLink}
              </a>
            </div>
          )}
          {err && <div className="auth-error">{err}</div>}
          {info && <div className="auth-info">{info}</div>}
          <div className="auth-actions-row">
            <button
              type="button"
              className="auth-btn ghost"
              onClick={onResend}
              disabled={busy}
            >
              Wyślij ponownie
            </button>
            <button
              type="button"
              className="auth-btn"
              onClick={() => {
                setView("login");
                setErr("");
                setInfo("");
              }}
            >
              Mam konto — zaloguj się
            </button>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="auth-wall">
      <div className="auth-card">
        <div className="auth-eyebrow">
          ▣ ZAZA/OS · {appLabel ? appLabel.toUpperCase() : "APLIKACJA"} · DLA
          ZALOGOWANYCH
        </div>
        <h2 className="auth-title">
          {view === "login" ? "Zaloguj się" : "Zarejestruj się"}
        </h2>
        <p className="auth-text-dim">
          Ta aplikacja jest dostępna tylko po zalogowaniu. Konto darmowe — email
          + hasło.
        </p>

        <div className="auth-tabs">
          <button
            type="button"
            className={`auth-tab ${view === "login" ? "active" : ""}`}
            onClick={() => {
              setView("login");
              setErr("");
              setInfo("");
            }}
          >
            LOGOWANIE
          </button>
          <button
            type="button"
            className={`auth-tab ${view === "register" ? "active" : ""}`}
            onClick={() => {
              setView("register");
              setErr("");
              setInfo("");
            }}
          >
            REJESTRACJA
          </button>
        </div>

        {view === "login" && (
          <form onSubmit={onLogin} className="auth-form">
            <label className="auth-label">EMAIL</label>
            <input
              className="auth-input"
              type="email"
              autoComplete="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="ty@example.com"
              required
              autoFocus
            />
            <label className="auth-label">HASŁO</label>
            <input
              className="auth-input"
              type="password"
              autoComplete="current-password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              required
            />
            {err && <div className="auth-error">{err}</div>}
            {info && <div className="auth-info">{info}</div>}
            <button className="auth-btn" type="submit" disabled={busy}>
              {busy ? "…" : "ZALOGUJ"}
            </button>
            <p className="auth-foot">
              Nie masz konta?{" "}
              <button
                type="button"
                className="auth-link"
                onClick={() => {
                  setView("register");
                  setErr("");
                  setInfo("");
                }}
              >
                Zarejestruj się
              </button>
            </p>
          </form>
        )}

        {view === "register" && (
          <form onSubmit={onRegister} className="auth-form">
            <label className="auth-label">EMAIL</label>
            <input
              className="auth-input"
              type="email"
              autoComplete="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="ty@example.com"
              required
              autoFocus
            />
            <label className="auth-label">HASŁO (min. 8 znaków)</label>
            <input
              className="auth-input"
              type="password"
              autoComplete="new-password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              required
              minLength={8}
            />
            <label className="auth-label">POWTÓRZ HASŁO</label>
            <input
              className="auth-input"
              type="password"
              autoComplete="new-password"
              value={password2}
              onChange={(e) => setPassword2(e.target.value)}
              required
              minLength={8}
            />
            {err && <div className="auth-error">{err}</div>}
            <button className="auth-btn" type="submit" disabled={busy}>
              {busy ? "…" : "ZAREJESTRUJ"}
            </button>
            <p className="auth-foot">
              Masz już konto?{" "}
              <button
                type="button"
                className="auth-link"
                onClick={() => {
                  setView("login");
                  setErr("");
                  setInfo("");
                }}
              >
                Zaloguj się
              </button>
            </p>
          </form>
        )}
      </div>
    </div>
  );
}

window.AuthWall = AuthWall;
