// Dados do app: carga dos JSON gerados por scripts/build_content.py, progresso e busca.

const JSON_CACHE = Object.create(null);
const LIDOS_KEY = "perseveranca.lidos";
const ULTIMO_KEY = "perseveranca.ultimo";

function fetchJson(url) {
  if (!JSON_CACHE[url]) {
    JSON_CACHE[url] = fetch(url)
      .then((response) => {
        if (!response.ok) throw new Error(`HTTP ${response.status} em ${url}`);
        return response.json();
      })
      .catch((error) => {
        delete JSON_CACHE[url];
        throw error;
      });
  }
  return JSON_CACHE[url];
}

const loadIndice = () => fetchJson("data/modulos.json");
const loadModulo = (slug) => fetchJson(`data/modulos/${slug}.json`);
const loadBusca = () => fetchJson("data/busca.json");

function useAsync(loader, deps) {
  const [state, setState] = React.useState({ data: null, error: null, loading: true });
  React.useEffect(() => {
    let alive = true;
    setState((prev) => ({ data: prev.data, error: null, loading: true }));
    loader()
      .then((data) => alive && setState({ data, error: null, loading: false }))
      .catch((error) => alive && setState({ data: null, error, loading: false }));
    return () => {
      alive = false;
    };
  }, deps);
  return state;
}

function readStorage(key, fallback) {
  try {
    const raw = localStorage.getItem(key);
    return raw ? JSON.parse(raw) : fallback;
  } catch (error) {
    return fallback;
  }
}

function writeStorage(key, value) {
  try {
    localStorage.setItem(key, JSON.stringify(value));
  } catch (error) {
    console.warn("Nao foi possivel salvar no aparelho:", error);
  }
}

function useLidos() {
  const [lidos, setLidos] = React.useState(() => new Set(readStorage(LIDOS_KEY, [])));
  const toggle = React.useCallback((slug, value) => {
    setLidos((prev) => {
      const next = new Set(prev);
      const marcar = value === undefined ? !next.has(slug) : value;
      if (marcar) next.add(slug);
      else next.delete(slug);
      writeStorage(LIDOS_KEY, Array.from(next));
      return next;
    });
  }, []);
  return [lidos, toggle];
}

const readUltimo = () => readStorage(ULTIMO_KEY, null);
const saveUltimo = (slug, pg) => writeStorage(ULTIMO_KEY, { slug, pg });

function normalizeText(value) {
  return String(value)
    .normalize("NFD")
    .replace(/[̀-ͯ]/g, "")
    .toLowerCase();
}

// Busca simples no texto integral: devolve modulo, pagina e um trecho em volta do termo.
function searchPages(entries, query, limit = 40) {
  const needle = normalizeText(query.trim());
  if (needle.length < 3) return [];
  const results = [];
  for (const entry of entries) {
    if (!entry._norm) entry._norm = normalizeText(entry.t);
    let index = entry._norm.indexOf(needle);
    if (index < 0) continue;
    let count = 0;
    const first = index;
    while (index >= 0) {
      count += 1;
      index = entry._norm.indexOf(needle, index + needle.length);
    }
    const start = Math.max(0, first - 60);
    const end = Math.min(entry.t.length, first + needle.length + 80);
    results.push({
      slug: entry.s,
      pg: entry.p,
      count,
      before: (start > 0 ? "…" : "") + entry.t.slice(start, first),
      match: entry.t.slice(first, first + needle.length),
      after: entry.t.slice(first + needle.length, end) + (end < entry.t.length ? "…" : ""),
    });
    if (results.length >= limit) break;
  }
  return results;
}

function groupModulos(modulos) {
  const groups = [];
  for (const mod of modulos) {
    const last = groups[groups.length - 1];
    if (last && last.grupo === mod.grupo) last.itens.push(mod);
    else groups.push({ grupo: mod.grupo, itens: [mod] });
  }
  return groups;
}

function pageRange(paginas) {
  if (!paginas || !paginas.length) return "";
  const first = paginas[0];
  const last = paginas[paginas.length - 1];
  return first === last ? `Pág. ${first}` : `Págs. ${first}–${last}`;
}

Object.assign(window, {
  loadIndice,
  loadModulo,
  loadBusca,
  useAsync,
  useLidos,
  readUltimo,
  saveUltimo,
  readStorage,
  writeStorage,
  searchPages,
  groupModulos,
  pageRange,
});
