/* ============================================================
   api.jsx — тонкий клиент боевого бэкенда (backend/server.py).

   Стратегия «backend-first + fallback»:
   · если сервер доступен (/api/health) — авторизация, прогресс и
     список пользователей идут через него (реальные данные, общие
     между устройствами);
   · если сервера нет — фронтенд продолжает работать на localStorage
     (демо-прототип), ничего не ломается.

   Адрес бэкенда (в порядке приоритета):
     1) window.AL_API_BASE           — задать <script> в html до api.jsx
     2) ?api=<url> в адресной строке  — удобно для теста
     3) localStorage "al_api_base"
     4) по умолчанию — тот же хост, порт 8000
   ============================================================ */

const AL_TOKEN_KEY = "al_token_v1";

function _resolveApiBase() {
  try {
    const q = new URLSearchParams(location.search).get("api");
    if (q) { try { localStorage.setItem("al_api_base", q); } catch (e) {} return q; }
  } catch (e) {}
  if (window.AL_API_BASE) return window.AL_API_BASE;
  try { const s = localStorage.getItem("al_api_base"); if (s) return s; } catch (e) {}
  const host = location.hostname || "localhost";
  const isLocal = host === "localhost" || host === "127.0.0.1" || host === "";
  // Локально — отдельный порт бэкенда; в бою по умолчанию тот же origin
  // (Caddy/nginx проксируют /api и /media на server.py) — тогда config.js
  // можно не трогать. Для схемы «сайт и API на разных доменах» задайте
  // window.AL_API_BASE в config.js.
  if (isLocal) { const proto = location.protocol === "https:" ? "https:" : "http:"; return proto + "//" + host + ":8000"; }
  return location.origin;
}
const API_BASE = _resolveApiBase().replace(/\/+$/, "");

/* ---- токен сессии ---- */
function getToken() { try { return localStorage.getItem(AL_TOKEN_KEY) || null; } catch (e) { return null; } }
function setToken(t) { try { t ? localStorage.setItem(AL_TOKEN_KEY, t) : localStorage.removeItem(AL_TOKEN_KEY); } catch (e) {} }

/* Возврат после OAuth (VK/Яндекс) или перехода по magic-link: бэкенд отправляет
   браузер на /learn.html#token=<jwt>. Ловим токен из hash, сохраняем и чистим URL. */
(function captureTokenFromHash() {
  try {
    const src = (location.hash || "") + "&" + (location.search || "");
    const m = src.match(/[#&?]token=([^&]+)/);
    if (m) {
      setToken(decodeURIComponent(m[1]));
      history.replaceState(null, "", location.pathname + (location.search.replace(/[?&]token=[^&]+/, "") || ""));
    }
  } catch (e) {}
})();

/* ---- проба доступности сервера (один раз, с таймаутом) ---- */
let _healthProbe = null;
function backendUp() {
  if (window.AL_FORCE_MOCK) return Promise.resolve(false);
  if (_healthProbe) return _healthProbe;
  _healthProbe = new Promise((resolve) => {
    const ctrl = ("AbortController" in window) ? new AbortController() : null;
    const timer = setTimeout(() => { if (ctrl) ctrl.abort(); resolve(false); }, 2500);
    fetch(API_BASE + "/api/health", { signal: ctrl ? ctrl.signal : undefined })
      .then((r) => { clearTimeout(timer); resolve(!!r && r.ok); })
      .catch(() => { clearTimeout(timer); resolve(false); });
  });
  return _healthProbe;
}
/* сбросить кэш пробы (напр. после того как сервер подняли) */
function resetBackendProbe() { _healthProbe = null; }

/* ---- базовый вызов; бросает Error с .code/.status при ошибке ---- */
async function apiFetch(path, opts) {
  opts = opts || {};
  const headers = Object.assign({}, opts.headers || {});
  const hasBody = opts.body !== undefined && opts.body !== null;
  if (hasBody && !opts.raw) headers["Content-Type"] = "application/json";
  if (opts.auth !== false) { const t = getToken(); if (t) headers["Authorization"] = "Bearer " + t; }
  if (opts.adminPass) headers["X-Admin-Password"] = opts.adminPass;

  const res = await fetch(API_BASE + path, {
    method: opts.method || "GET",
    headers,
    body: hasBody ? (opts.raw ? opts.body : JSON.stringify(opts.body)) : undefined,
  });
  let data = null;
  try { data = await res.json(); } catch (e) {}
  if (!res.ok) {
    const err = new Error((data && data.error) || ("http_" + res.status));
    err.code = (data && data.error) || ("http_" + res.status);
    err.status = res.status;
    throw err;
  }
  return data;
}
const apiGet  = (path, opts) => apiFetch(path, Object.assign({ method: "GET" }, opts));
const apiPost = (path, body, opts) => apiFetch(path, Object.assign({ method: "POST", body }, opts));
const apiPut  = (path, body, opts) => apiFetch(path, Object.assign({ method: "PUT", body }, opts));

/* абсолютный адрес медиа с бэкенда (для <video src>) */
function mediaUrl(u) {
  if (!u) return u;
  return /^https?:\/\//.test(u) ? u : (API_BASE + (u[0] === "/" ? "" : "/") + u);
}

Object.assign(window, {
  API_BASE, AL_TOKEN_KEY, getToken, setToken, backendUp, resetBackendProbe,
  apiFetch, apiGet, apiPost, apiPut, mediaUrl,
});
