const I18N = {
  es: {
    ui: {
      eyebrow: "base de datos comunitaria",
      subtitle:
        "Visualizador de <code>games.json</code> con filtros por nombre, fecha, idiomas y flags.",
      stat: { total: "Total", visible: "Visibles", history: "Con historial" },
      filters: {
        title: "Filtros",
        search: "Buscar",
        searchPlaceholder: "titulo, title id, idioma",
        dateFrom: "Fecha desde",
        dateTo: "Fecha hasta",
        languages: "Idiomas",
        platforms: "Plataforma",
        blocks: "Bloque de alta",
        flags: "Flags",
        tags: "Tags",
        events: "Eventos",
        multi: "Multi-idioma",
        showHidden: "Ocultos",
        showHiddenToggle: "Mostrar ocultos",
        showFavorites: "Favoritos",
        showFavoritesToggle: "Solo favoritos",
      },
      actions: { reset: "Reset" },
      loading: "Cargando games.json...",
      buttons: {
        loadPersonal: "Cargar personalizacion",
        savePersonal: "Guardar personalizacion",
        loadGames: "Cargar games.json",
        loadExtra: "Cargar info extra",
        saveExtra: "Guardar info extra",
      },
      reminder: {
        default: "Recuerda exportar tu personalizacion cuando hagas cambios.",
        unsaved:
          "Cambios sin exportar: recuerda guardar tu personalizacion.",
        saved: "Personalizacion exportada.",
        loaded: "Personalizacion cargada.",
      },
      status: { showing: "Mostrando {visible} de {total}" },
      row: {
        titleIds: "title_ids",
        platforms: "plataforma",
        languages: "idiomas",
        flags: "flags",
        lastDate: "ultima fecha",
        noResults: "Sin resultados.",
      },
      controls: {
        played: "Jugado",
        owned: "Lo tengo",
        rating: "Puntuacion",
        favorite: "Favorito",
        hidden: "Ocultar",
        notes: "Notas",
      },
      extra: {
        summary: "De que va",
        tags: "Tags (accion, aventura...)",
        rating: "Rating",
        review: "Review",
        description: "Descripcion",
      },
      links: {
        metacritic: "Metacritic",
        steam: "Steam",
        youtube: "YouTube",
        eshop: "Nintendo eShop",
      },
      errors: {
        invalidJson: "JSON invalido.",
        invalidGames: "JSON invalido: selecciona el games.json generado.",
        invalidPersonal: "JSON de personalizacion invalido.",
        invalidExtra: "JSON de info extra invalido.",
        fileMode:
          "Estas abriendo el HTML por file://. Usa Cargar JSON local o levanta un servidor.",
        noGames: "No pude cargar games.json. Usa Cargar JSON local.",
      },
    },
  },
  en: {
    ui: {
      eyebrow: "community database",
      subtitle:
        "Viewer for <code>games.json</code> with filters by name, date, languages and flags.",
      stat: { total: "Total", visible: "Visible", history: "With history" },
      filters: {
        title: "Filters",
        search: "Search",
        searchPlaceholder: "title, title id, language",
        dateFrom: "Date from",
        dateTo: "Date to",
        languages: "Languages",
        platforms: "Platform",
        blocks: "Added block",
        flags: "Flags",
        tags: "Tags",
        events: "Events",
        multi: "Multi-language",
        showHidden: "Hidden",
        showHiddenToggle: "Show hidden",
        showFavorites: "Favorites",
        showFavoritesToggle: "Only favorites",
      },
      actions: { reset: "Reset" },
      loading: "Loading games.json...",
      buttons: {
        loadPersonal: "Load personalization",
        savePersonal: "Save personalization",
        loadGames: "Load games.json",
        loadExtra: "Load extra info",
        saveExtra: "Save extra info",
      },
      reminder: {
        default: "Remember to export your personalization when you make changes.",
        unsaved: "Unsaved changes: remember to export your personalization.",
        saved: "Personalization exported.",
        loaded: "Personalization loaded.",
      },
      status: { showing: "Showing {visible} of {total}" },
      row: {
        titleIds: "title_ids",
        platforms: "platform",
        languages: "languages",
        flags: "flags",
        lastDate: "last date",
        noResults: "No results.",
      },
      controls: {
        played: "Played",
        owned: "Owned",
        rating: "Rating",
        favorite: "Favorite",
        hidden: "Hide",
        notes: "Notes",
      },
      extra: {
        summary: "What it's about",
        tags: "Tags (action, adventure...)",
        rating: "Rating",
        review: "Review",
        description: "Description",
      },
      links: {
        metacritic: "Metacritic",
        steam: "Steam",
        youtube: "YouTube",
        eshop: "Nintendo eShop",
      },
      errors: {
        invalidJson: "Invalid JSON.",
        invalidGames: "Invalid JSON: select the generated games.json.",
        invalidPersonal: "Invalid personalization JSON.",
        invalidExtra: "Invalid extra info JSON.",
        fileMode:
          "You are opening the HTML via file://. Use Load games.json or run a server.",
        noGames: "Could not load games.json. Use Load games.json.",
      },
    },
  },
  ru: {
    ui: {
      eyebrow: "база данных сообщества",
      subtitle:
        "Просмотр <code>games.json</code> с фильтрами по названию, дате, языкам и флагам.",
      stat: { total: "Всего", visible: "Видимых", history: "С историей" },
      filters: {
        title: "Фильтры",
        search: "Поиск",
        searchPlaceholder: "название, title id, язык",
        dateFrom: "Дата от",
        dateTo: "Дата до",
        languages: "Языки",
        platforms: "Платформа",
        blocks: "Блок добавления",
        flags: "Флаги",
        tags: "Теги",
        events: "События",
        multi: "Мультиязычный",
        showHidden: "Скрытые",
        showHiddenToggle: "Показать скрытые",
        showFavorites: "Избранное",
        showFavoritesToggle: "Только избранные",
      },
      actions: { reset: "Сброс" },
      loading: "Загрузка games.json...",
      buttons: {
        loadPersonal: "Загрузить персонализацию",
        savePersonal: "Сохранить персонализацию",
        loadGames: "Загрузить games.json",
        loadExtra: "Загрузить доп. инфо",
        saveExtra: "Сохранить доп. инфо",
      },
      reminder: {
        default: "Не забудьте экспортировать персонализацию после изменений.",
        unsaved: "Есть несохраненные изменения: экспортируйте персонализацию.",
        saved: "Персонализация экспортирована.",
        loaded: "Персонализация загружена.",
      },
      status: { showing: "Показано {visible} из {total}" },
      row: {
        titleIds: "title_ids",
        platforms: "платформа",
        languages: "языки",
        flags: "флаги",
        lastDate: "последняя дата",
        noResults: "Нет результатов.",
      },
      controls: {
        played: "Пройдено",
        owned: "Есть",
        rating: "Оценка",
        favorite: "Избранное",
        hidden: "Скрыть",
        notes: "Заметки",
      },
      extra: {
        summary: "О чем игра",
        tags: "Теги (экшен, приключения...)",
        rating: "Рейтинг",
        review: "Отзыв",
        description: "Description",
      },
      links: {
        metacritic: "Metacritic",
        steam: "Steam",
        youtube: "YouTube",
        eshop: "Nintendo eShop",
      },
      errors: {
        invalidJson: "Некорректный JSON.",
        invalidGames: "Некорректный JSON: выберите games.json.",
        invalidPersonal: "Некорректный JSON персонализации.",
        invalidExtra: "Некорректный JSON доп. инфо.",
        fileMode:
          "HTML открыт через file://. Используйте Загрузить games.json или запустите сервер.",
        noGames: "Не удалось загрузить games.json. Используйте Загрузить games.json.",
      },
    },
  },
};

const LANG_KEY = "switch_library_lang";
let currentLang = "es";
let reminderState = "default";
const overlayState = { gameIndex: 0, imageIndex: 0 };
let overlayEls = null;

function t(key, vars = {}) {
  const parts = key.split(".");
  let obj = I18N[currentLang] || I18N.es;
  for (const part of parts) {
    obj = obj && obj[part];
  }
  if (!obj || typeof obj !== "string") return key;
  return obj.replace(/\{(\w+)\}/g, (_, name) => vars[name] ?? "");
}

function applyTranslations() {
  document.querySelectorAll("[data-i18n]").forEach((el) => {
    el.textContent = t(el.dataset.i18n);
  });
  document.querySelectorAll("[data-i18n-html]").forEach((el) => {
    el.innerHTML = t(el.dataset.i18nHtml);
  });
  document.querySelectorAll("[data-i18n-placeholder]").forEach((el) => {
    el.setAttribute("placeholder", t(el.dataset.i18nPlaceholder));
  });
  updateReminder(reminderState);
}

function setLanguage(lang) {
  if (!I18N[lang]) return;
  currentLang = lang;
  localStorage.setItem(LANG_KEY, lang);
  document
    .querySelectorAll(".lang-btn")
    .forEach((btn) => btn.classList.toggle("active", btn.dataset.lang === lang));
  applyTranslations();
  if (state.games.length) {
    applyFilters();
  }
}

function updateReminder(state) {
  reminderState = state;
  if (reminderEl) {
    reminderEl.textContent = t(`ui.reminder.${state}`);
  }
}

const state = {
  games: [],
  filtered: [],
  filters: {
    q: "",
    dateFrom: "",
    dateTo: "",
    languages: new Set(),
    multiLang: false,
    showHidden: false,
    showFavorites: false,
    platforms: new Set(),
    blocks: new Set(),
    flags: new Set(),
    tags: new Set(),
    events: new Set(),
  },
};

const listEl = document.getElementById("list");
const statusEl = document.getElementById("status");
const loadingEl = document.getElementById("loading");
const statTotal = document.getElementById("stat-total");
const statVisible = document.getElementById("stat-visible");
const statHistory = document.getElementById("stat-history");
const fileBtn = document.getElementById("load-file");
const fileInput = document.getElementById("file-input");
const userLoadBtn = document.getElementById("load-user-data");
const userSaveBtn = document.getElementById("save-user-data");
const userFileInput = document.getElementById("user-file-input");
const extraLoadBtn = document.getElementById("load-extra-info");
const extraSaveBtn = document.getElementById("save-extra-info");
const extraFileInput = document.getElementById("extra-file-input");

const USER_STORAGE_KEY = "switch_library_user_data";
const EXTRA_STORAGE_KEY = "switch_library_extra_info";
const USER_DB_NAME = "switch_library_db";
const USER_DB_STORE = "settings";
const USER_DB_VERSION = 1;
let userData = { games: {} };
let hasUnsavedChanges = false;
let extraInfo = { games: {} };
const reminderEl = document.getElementById("reminder");

const langsEl = document.getElementById("langs");
const platformsEl = document.getElementById("platforms");
const blocksEl = document.getElementById("blocks");
const flagsEl = document.getElementById("flags");
const eventsEl = document.getElementById("events");
const tagsFilterEl = document.getElementById("tags-filter");

function toDate(value) {
  if (!value) return null;
  return new Date(value + "T00:00:00");
}

function lastEventDate(history) {
  if (!history || history.length === 0) return null;
  const dates = history
    .map((h) => h.date)
    .filter(Boolean)
    .sort();
  return dates.length ? dates[dates.length - 1] : null;
}

function collectFacet(set, value) {
  if (!value) return;
  if (Array.isArray(value)) {
    value.forEach((v) => v && set.add(v));
  } else {
    set.add(value);
  }
}

function buildFacetChips(container, items, onToggle) {
  container.innerHTML = "";
  items.sort().forEach((item) => {
    const label = document.createElement("label");
    label.className = "chip";
    const input = document.createElement("input");
    input.type = "checkbox";
    input.value = item;
    input.addEventListener("change", () => {
      label.classList.toggle("active", input.checked);
      onToggle(input.value, input.checked);
    });
    const span = document.createElement("span");
    span.textContent = item;
    label.appendChild(input);
    label.appendChild(span);
    container.appendChild(label);
  });
}

function renderList(items) {
  listEl.innerHTML = "";
  if (items.length === 0) {
    listEl.innerHTML = `<div class='row'>${t("ui.row.noResults")}</div>`;
    return;
  }

  const frag = document.createDocumentFragment();
  items.forEach((game) => {
    const row = document.createElement("div");
    row.className = "row";
    row.dataset.gameId = game.id;

    const rowMain = document.createElement("div");
    rowMain.className = "row-main";

    const meta = document.createElement("div");
    meta.className = "meta";
    const ids = (game.title_ids || []).join(", ");
    const platform = game.platform || "-";
    const langList = (game.languages || []).filter(Boolean);
    const flagCount = (game.flags || []).length;
    const lastDate = lastEventDate(game.history);
    meta.innerHTML = `
      <span>${game.id}</span>
      <span>${t("ui.row.titleIds")}: ${ids || "-"}</span>
      <span>${t("ui.row.platforms")}: ${platform}</span>
      <span>${t("ui.row.languages")}: ${langList.length ? langList.join(", ") : "-"}</span>
      <span>${t("ui.row.flags")}: ${flagCount}</span>
      <span>${t("ui.row.lastDate")}: ${lastDate || "-"}</span>
    `;

    const tags = document.createElement("div");
    tags.className = "history";
    const eventTypes = new Set((game.history || []).map((h) => h.event));
    eventTypes.forEach((ev) => {
      const t = document.createElement("span");
      t.textContent = ev;
      tags.appendChild(t);
    });

    const links = document.createElement("div");
    links.className = "history";
    const q = encodeURIComponent(game.title);
    const mkLink = (label, url) => {
      const a = document.createElement("a");
      a.href = url;
      a.target = "_blank";
      a.rel = "noopener";
      a.textContent = label;
      a.className = "tag";
      return a;
    };
    links.appendChild(mkLink(t("ui.links.metacritic"), `https://www.metacritic.com/search/${q}/`));
    links.appendChild(mkLink(t("ui.links.steam"), `https://store.steampowered.com/search/?term=${q}`));
    links.appendChild(mkLink(t("ui.links.youtube"), `https://www.youtube.com/results?search_query=${q}+switch`));
    links.appendChild(mkLink(t("ui.links.eshop"), `https://www.nintendo.com/search/#q=${q}`));

    const controls = document.createElement("div");
    controls.className = "controls";
    const user = userData.games[game.id] || {};

    const favBtn = document.createElement("button");
    favBtn.type = "button";
    favBtn.className = "star-btn";
    favBtn.textContent = "★";
    favBtn.classList.toggle("active", Boolean(user.favorite));
    favBtn.title = t("ui.controls.favorite");
    favBtn.addEventListener("click", () => {
      const next = !Boolean(userData.games[game.id]?.favorite);
      setUserData(game.id, { favorite: next });
      favBtn.classList.toggle("active", next);
      applyFilters();
    });

    const playedLabel = document.createElement("label");
    playedLabel.className = "control-pill";
    const playedInput = document.createElement("input");
    playedInput.type = "checkbox";
    playedInput.checked = Boolean(user.played);
    playedInput.addEventListener("change", () => {
      setUserData(game.id, { played: playedInput.checked });
    });
    const playedText = document.createElement("span");
    playedText.textContent = t("ui.controls.played");
    playedLabel.appendChild(playedInput);
    playedLabel.appendChild(playedText);

    const ownedLabel = document.createElement("label");
    ownedLabel.className = "control-pill";
    const ownedInput = document.createElement("input");
    ownedInput.type = "checkbox";
    ownedInput.checked = Boolean(user.owned);
    ownedInput.addEventListener("change", () => {
      setUserData(game.id, { owned: ownedInput.checked });
    });
    const ownedText = document.createElement("span");
    ownedText.textContent = t("ui.controls.owned");
    ownedLabel.appendChild(ownedInput);
    ownedLabel.appendChild(ownedText);

    const ratingLabel = document.createElement("label");
    ratingLabel.className = "control-pill";
    const ratingText = document.createElement("span");
    ratingText.textContent = t("ui.controls.rating");
    const ratingSelect = document.createElement("select");
    const emptyOption = document.createElement("option");
    emptyOption.value = "";
    emptyOption.textContent = "-";
    ratingSelect.appendChild(emptyOption);
    for (let i = 1; i <= 10; i += 1) {
      const opt = document.createElement("option");
      opt.value = String(i);
      opt.textContent = String(i);
      ratingSelect.appendChild(opt);
    }
    ratingSelect.value = user.rating ? String(user.rating) : "";
    ratingSelect.addEventListener("change", () => {
      const val = ratingSelect.value ? Number(ratingSelect.value) : null;
      setUserData(game.id, { rating: val });
    });
    ratingLabel.appendChild(ratingText);
    ratingLabel.appendChild(ratingSelect);

    const hiddenLabel = document.createElement("label");
    hiddenLabel.className = "control-pill";
    const hiddenInput = document.createElement("input");
    hiddenInput.type = "checkbox";
    hiddenInput.checked = Boolean(user.hidden);
    hiddenInput.addEventListener("change", () => {
      setUserData(game.id, { hidden: hiddenInput.checked });
      if (!state.filters.showHidden && hiddenInput.checked) {
        applyFilters();
      }
    });
    const hiddenText = document.createElement("span");
    hiddenText.textContent = t("ui.controls.hidden");
    hiddenLabel.appendChild(hiddenInput);
    hiddenLabel.appendChild(hiddenText);

    controls.appendChild(favBtn);
    controls.appendChild(playedLabel);
    controls.appendChild(ownedLabel);
    controls.appendChild(ratingLabel);
    controls.appendChild(hiddenLabel);

    const baseExtra = game.extra && typeof game.extra === "object" ? game.extra : {};
    const storedExtra = extraInfo.games[game.id] || {};
    const extra = { ...baseExtra, ...storedExtra };

    const summaryBox = document.createElement("div");
    summaryBox.className = "extra-panel";
    const summaryLabel = document.createElement("div");
    summaryLabel.className = "label";
    summaryLabel.textContent = t("ui.extra.summary");
    const summaryVal = document.createElement("div");
    summaryVal.className = "value";
    summaryVal.textContent = extra.summary || "-";
    summaryVal.classList.toggle("empty", !extra.summary);
    summaryBox.appendChild(summaryLabel);
    summaryBox.appendChild(summaryVal);

    const descBox = document.createElement("div");
    descBox.className = "extra-panel";
    const descLabel = document.createElement("div");
    descLabel.className = "label";
    descLabel.textContent = t("ui.extra.description");
    const descVal = document.createElement("div");
    descVal.className = "value";
    descVal.textContent = extra.description || "-";
    descVal.classList.toggle("empty", !extra.description);
    descBox.appendChild(descLabel);
    descBox.appendChild(descVal);

    const reviewBox = document.createElement("div");
    reviewBox.className = "extra-panel";
    const reviewLabel = document.createElement("div");
    reviewLabel.className = "label";
    reviewLabel.textContent = t("ui.extra.review");
    const reviewVal = document.createElement("div");
    reviewVal.className = "value";
    reviewVal.textContent = extra.review || "-";
    reviewVal.classList.toggle("empty", !extra.review);
    reviewBox.appendChild(reviewLabel);
    reviewBox.appendChild(reviewVal);

    const tagsRow = document.createElement("div");
    tagsRow.className = "extra-tags";
    const gameTags = normalizeTags(extra.tags);
    if (gameTags.length) {
      gameTags.forEach((tag) => {
        const span = document.createElement("span");
        span.className = "tag tag-click";
        span.textContent = tag;
        span.addEventListener("click", () => {
          state.filters.tags.add(tag);
          syncTagFilterSelection(tag);
          applyFilters();
        });
        tagsRow.appendChild(span);
      });
    }

    const titleRow = document.createElement("div");
    titleRow.className = "title-row";
    const title = document.createElement("h3");
    title.textContent = game.title;
    const ratingBadge = document.createElement("div");
    ratingBadge.className = "rating-badge";
    ratingBadge.textContent = extra.rating != null ? `${extra.rating}/100` : "-/-";
    titleRow.appendChild(title);
    titleRow.appendChild(ratingBadge);

    rowMain.appendChild(titleRow);
    rowMain.appendChild(meta);
    rowMain.appendChild(tags);
    rowMain.appendChild(links);
    rowMain.appendChild(controls);
    if (extra.summary) rowMain.appendChild(summaryBox);
    if (extra.description) rowMain.appendChild(descBox);
    if (extra.review) rowMain.appendChild(reviewBox);
    if (gameTags.length) rowMain.appendChild(tagsRow);

    const imagesData = window.IMAGES_DATA || {};
    const imageEntry = imagesData[game.id];
    let slider = null;
    if (imageEntry && Array.isArray(imageEntry.images) && imageEntry.images.length) {
      row.classList.add("has-image");
      slider = document.createElement("div");
      slider.className = "image-slider";
      let idx = 0;
      const img = document.createElement("img");
      img.alt = game.title;
      const setImg = () => {
        const current = imageEntry.images[idx];
        img.src = current.path || current.url || "";
      };
      setImg();
      img.addEventListener("click", () => {
        openOverlay(game.id);
      });

      const left = document.createElement("button");
      left.type = "button";
      left.className = "btn btn-secondary";
      left.textContent = "‹";
      left.addEventListener("click", () => {
        idx = (idx - 1 + imageEntry.images.length) % imageEntry.images.length;
        setImg();
        updateCounter();
      });

      const right = document.createElement("button");
      right.type = "button";
      right.className = "btn btn-secondary";
      right.textContent = "›";
      right.addEventListener("click", () => {
        idx = (idx + 1) % imageEntry.images.length;
        setImg();
        updateCounter();
      });

      const counter = document.createElement("span");
      counter.className = "muted";
      const updateCounter = () => {
        counter.textContent = `${idx + 1}/${imageEntry.images.length}`;
      };
      updateCounter();

      slider.appendChild(left);
      slider.appendChild(img);
      slider.appendChild(right);
      slider.appendChild(counter);

      const notes = document.createElement("textarea");
      notes.className = "comment-box slider-notes";
      notes.placeholder = t("ui.controls.notes");
      notes.value = user.notes || "";
      notes.addEventListener("input", () => {
        setUserData(game.id, { notes: notes.value.trim() });
      });
      slider.appendChild(notes);
    }

    row.appendChild(rowMain);
    if (slider) {
      row.appendChild(slider);
    } else {
      const notes = document.createElement("textarea");
      notes.className = "comment-box";
      notes.placeholder = t("ui.controls.notes");
      notes.value = user.notes || "";
      notes.addEventListener("input", () => {
        setUserData(game.id, { notes: notes.value.trim() });
      });
      row.appendChild(notes);
    }
    frag.appendChild(row);
  });

  listEl.appendChild(frag);
}

function updateStatus(message) {
  statusEl.textContent = message || "";
}

function getCurrentList() {
  return state.filtered.length ? state.filtered : state.games;
}

function buildOverlay() {
  const overlay = document.createElement("div");
  overlay.className = "image-overlay";
  overlay.addEventListener("click", (e) => {
    if (e.target === overlay) {
      overlay.style.display = "none";
    }
  });

  const header = document.createElement("div");
  header.className = "overlay-header";

  const prevGame = document.createElement("button");
  prevGame.type = "button";
  prevGame.className = "btn btn-secondary overlay-nav";
  prevGame.textContent = "‹";

  const headerText = document.createElement("div");
  const titleEl = document.createElement("div");
  titleEl.className = "overlay-title";
  const metaEl = document.createElement("div");
  metaEl.className = "overlay-meta";
  headerText.appendChild(titleEl);
  headerText.appendChild(metaEl);

  const nextGame = document.createElement("button");
  nextGame.type = "button";
  nextGame.className = "btn btn-secondary overlay-nav";
  nextGame.textContent = "›";

  header.appendChild(prevGame);
  header.appendChild(headerText);
  header.appendChild(nextGame);

  const wrap = document.createElement("div");
  wrap.className = "overlay-wrap";
  const leftImg = document.createElement("button");
  leftImg.type = "button";
  leftImg.className = "btn btn-secondary overlay-nav";
  leftImg.textContent = "‹";
  const img = document.createElement("img");
  const rightImg = document.createElement("button");
  rightImg.type = "button";
  rightImg.className = "btn btn-secondary overlay-nav";
  rightImg.textContent = "›";
  wrap.appendChild(leftImg);
  wrap.appendChild(img);
  wrap.appendChild(rightImg);

  const counter = document.createElement("div");
  counter.className = "overlay-counter";

  overlay.appendChild(header);
  overlay.appendChild(wrap);
  overlay.appendChild(counter);
  document.body.appendChild(overlay);

  overlayEls = {
    overlay,
    titleEl,
    metaEl,
    prevGame,
    nextGame,
    leftImg,
    rightImg,
    img,
    counter,
  };
}

function updateOverlay() {
  const list = getCurrentList();
  const game = list[overlayState.gameIndex];
  if (!game || !overlayEls) return;

  overlayEls.titleEl.textContent = game.title;
  const ids = (game.title_ids || []).join(", ") || "-";
  const platform = game.platform || "-";
  const langList = (game.languages || []).filter(Boolean);
  const flagCount = (game.flags || []).length;
  const lastDate = lastEventDate(game.history) || "-";
  overlayEls.metaEl.textContent = `${game.id} • ${t("ui.row.titleIds")}: ${ids} • ${t(
    "ui.row.platforms"
  )}: ${platform} • ${t("ui.row.languages")}: ${
    langList.length ? langList.join(", ") : "-"
  } • ${t("ui.row.flags")}: ${flagCount} • ${t("ui.row.lastDate")}: ${lastDate}`;

  const imagesData = window.IMAGES_DATA || {};
  const entry = imagesData[game.id];
  const images = entry && Array.isArray(entry.images) ? entry.images : [];
  if (images.length === 0) {
    overlayEls.img.src = "";
    overlayEls.counter.textContent = "0/0";
    scrollToGame(game.id);
    return;
  }
  overlayState.imageIndex = Math.max(
    0,
    Math.min(overlayState.imageIndex, images.length - 1)
  );
  const current = images[overlayState.imageIndex];
  overlayEls.img.src = current.path || current.url || "";
  overlayEls.counter.textContent = `${overlayState.imageIndex + 1}/${images.length}`;
  scrollToGame(game.id);
}

function openOverlay(gameId) {
  if (!overlayEls) {
    buildOverlay();
  }
  const list = getCurrentList();
  const idx = list.findIndex((g) => g.id === gameId);
  overlayState.gameIndex = idx >= 0 ? idx : 0;
  overlayState.imageIndex = 0;
  updateOverlay();
  overlayEls.overlay.style.display = "flex";
}

function bindOverlayControls() {
  if (!overlayEls) return;
  overlayEls.prevGame.addEventListener("click", () => {
    const list = getCurrentList();
    overlayState.gameIndex =
      (overlayState.gameIndex - 1 + list.length) % list.length;
    overlayState.imageIndex = 0;
    updateOverlay();
  });
  overlayEls.nextGame.addEventListener("click", () => {
    const list = getCurrentList();
    overlayState.gameIndex = (overlayState.gameIndex + 1) % list.length;
    overlayState.imageIndex = 0;
    updateOverlay();
  });
  overlayEls.leftImg.addEventListener("click", () => {
    const list = getCurrentList();
    const game = list[overlayState.gameIndex];
    const imagesData = window.IMAGES_DATA || {};
    const entry = imagesData[game.id];
    const images = entry && Array.isArray(entry.images) ? entry.images : [];
    if (!images.length) return;
    overlayState.imageIndex =
      (overlayState.imageIndex - 1 + images.length) % images.length;
    updateOverlay();
  });
  overlayEls.rightImg.addEventListener("click", () => {
    const list = getCurrentList();
    const game = list[overlayState.gameIndex];
    const imagesData = window.IMAGES_DATA || {};
    const entry = imagesData[game.id];
    const images = entry && Array.isArray(entry.images) ? entry.images : [];
    if (!images.length) return;
    overlayState.imageIndex = (overlayState.imageIndex + 1) % images.length;
    updateOverlay();
  });

  document.addEventListener("keydown", (e) => {
    if (!overlayEls || overlayEls.overlay.style.display !== "flex") return;
    if (e.key === "ArrowLeft") {
      overlayEls.leftImg.click();
    } else if (e.key === "ArrowRight") {
      overlayEls.rightImg.click();
    } else if (e.key === "Escape") {
      overlayEls.overlay.style.display = "none";
    }
  });
}

function scrollToGame(gameId) {
  requestAnimationFrame(() => {
    const row = document.querySelector(`[data-game-id="${gameId}"]`);
    if (row) {
      row.scrollIntoView({ block: "center", behavior: "smooth" });
    }
  });
}

function matchQuery(game, q) {
  if (!q) return true;
  const needle = q.toLowerCase();
  const hay = [
    game.title,
    game.id,
    ...(game.title_ids || []),
    ...(game.languages || []),
    ...(game.flags || []),
  ]
    .join(" ")
    .toLowerCase();
  return hay.includes(needle);
}

function matchDate(game, from, to) {
  if (!from && !to) return true;
  const dateStr = lastEventDate(game.history);
  if (!dateStr) return false;
  const dt = new Date(dateStr + "T00:00:00");
  if (from && dt < from) return false;
  if (to && dt > to) return false;
  return true;
}

function matchSet(items, selected) {
  if (selected.size === 0) return true;
  const set = new Set(items || []);
  for (const s of selected) {
    if (!set.has(s)) return false;
  }
  return true;
}

function matchLanguages(items, selected, multiLang) {
  const langs = items || [];
  if (selected.size === 0) {
    return multiLang ? langs.length > 1 : true;
  }
  const set = new Set(langs);
  for (const s of selected) {
    if (!set.has(s)) return false;
  }
  return multiLang ? langs.length > 1 : true;
}

function matchEvents(history, selected) {
  if (selected.size === 0) return true;
  const set = new Set((history || []).map((h) => h.event));
  for (const s of selected) {
    if (!set.has(s)) return false;
  }
  return true;
}

function normalizeTags(tags) {
  if (!tags) return [];
  if (Array.isArray(tags)) {
    return tags.map((t) => String(t).trim()).filter(Boolean);
  }
  return String(tags)
    .split(/[|,]/)
    .map((t) => t.trim())
    .filter(Boolean);
}

function syncTagFilterSelection(tag) {
  if (!tagsFilterEl) return;
  const value = CSS && CSS.escape ? CSS.escape(tag) : tag;
  tagsFilterEl.querySelectorAll(`input[value="${value}"]`).forEach((input) => {
    input.checked = true;
    if (input.parentElement) {
      input.parentElement.classList.add("active");
    }
  });
}

function applyFilters() {
  const from = toDate(state.filters.dateFrom);
  const to = toDate(state.filters.dateTo);
  state.filtered = state.games.filter((game) => {
    const user = userData.games[game.id] || {};
    if (user.hidden && !state.filters.showHidden) {
      return false;
    }
    if (state.filters.showFavorites && !user.favorite) {
      return false;
    }
    const extra = game.extra || {};
    const gameTags = normalizeTags(extra.tags);
    return (
      matchQuery(game, state.filters.q) &&
      matchDate(game, from, to) &&
      matchLanguages(game.languages, state.filters.languages, state.filters.multiLang) &&
      matchSet([game.platform], state.filters.platforms) &&
      matchSet(game.added_in_blocks, state.filters.blocks) &&
      matchSet(game.flags, state.filters.flags) &&
      matchEvents(game.history, state.filters.events) &&
      matchSet(gameTags, state.filters.tags)
    );
  });
  state.filtered.sort((a, b) => {
    const favA = Boolean(userData.games[a.id]?.favorite);
    const favB = Boolean(userData.games[b.id]?.favorite);
    if (favA === favB) return 0;
    return favA ? -1 : 1;
  });
  statVisible.textContent = state.filtered.length;
  renderList(state.filtered);
  updateStatus(
    t("ui.status.showing", {
      visible: state.filtered.length,
      total: state.games.length,
    })
  );
}

function bindInputs() {
  document.getElementById("q").addEventListener("input", (e) => {
    state.filters.q = e.target.value.trim();
    applyFilters();
  });

  document.getElementById("date-from").addEventListener("change", (e) => {
    state.filters.dateFrom = e.target.value;
    applyFilters();
  });

  document.getElementById("date-to").addEventListener("change", (e) => {
    state.filters.dateTo = e.target.value;
    applyFilters();
  });

  document.getElementById("reset").addEventListener("click", () => {
    state.filters = {
      q: "",
      dateFrom: "",
      dateTo: "",
      languages: new Set(),
      multiLang: false,
      showHidden: false,
      showFavorites: false,
      platforms: new Set(),
      blocks: new Set(),
      flags: new Set(),
      tags: new Set(),
      events: new Set(),
    };
    document.getElementById("q").value = "";
    document.getElementById("date-from").value = "";
    document.getElementById("date-to").value = "";
    document
      .querySelectorAll(".chip input")
      .forEach((input) => (input.checked = false));
    document
      .querySelectorAll(".chip")
      .forEach((chip) => chip.classList.remove("active"));
    const multi = document.getElementById("lang-multi");
    multi.checked = false;
    document.getElementById("lang-multi-chip").classList.remove("active");
    const showHidden = document.getElementById("show-hidden");
    showHidden.checked = false;
    document.getElementById("show-hidden-chip").classList.remove("active");
    const showFavorites = document.getElementById("show-favorites");
    showFavorites.checked = false;
    document.getElementById("show-favorites-chip").classList.remove("active");
    applyFilters();
  });
}

function openUserDb() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(USER_DB_NAME, USER_DB_VERSION);
    request.onupgradeneeded = () => {
      const db = request.result;
      if (!db.objectStoreNames.contains(USER_DB_STORE)) {
        db.createObjectStore(USER_DB_STORE);
      }
    };
    request.onsuccess = () => resolve(request.result);
    request.onerror = () => reject(request.error);
  });
}

async function loadUserDataFromStorage() {
  try {
    const db = await openUserDb();
    const tx = db.transaction(USER_DB_STORE, "readonly");
    const store = tx.objectStore(USER_DB_STORE);
    const request = store.get(USER_STORAGE_KEY);
    const data = await new Promise((resolve, reject) => {
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
    if (data && typeof data === "object" && data.games) {
      userData = { games: data.games };
    }
  } catch (err) {
    console.error(err);
  }
}

async function saveUserDataToStorage() {
  try {
    const db = await openUserDb();
    const tx = db.transaction(USER_DB_STORE, "readwrite");
    const store = tx.objectStore(USER_DB_STORE);
    store.put(userData, USER_STORAGE_KEY);
  } catch (err) {
    console.error(err);
  }
}

async function loadExtraInfoFromStorage() {
  try {
    const db = await openUserDb();
    const tx = db.transaction(USER_DB_STORE, "readonly");
    const store = tx.objectStore(USER_DB_STORE);
    const request = store.get(EXTRA_STORAGE_KEY);
    const data = await new Promise((resolve, reject) => {
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
    if (data && typeof data === "object" && data.games) {
      extraInfo = { games: data.games };
    }
  } catch (err) {
    console.error(err);
  }
}

async function saveExtraInfoToStorage() {
  try {
    const db = await openUserDb();
    const tx = db.transaction(USER_DB_STORE, "readwrite");
    const store = tx.objectStore(USER_DB_STORE);
    store.put(extraInfo, EXTRA_STORAGE_KEY);
  } catch (err) {
    console.error(err);
  }
}

function setExtraInfo(gameId, patch) {
  const current = extraInfo.games[gameId] || {};
  const next = { ...current, ...patch };
  const keep =
    (next.summary && next.summary.length > 0) ||
    (next.tags && next.tags.length > 0) ||
    next.rating ||
    (next.review && next.review.length > 0) ||
    (next.description && next.description.length > 0);
  if (keep) {
    extraInfo.games[gameId] = next;
  } else {
    delete extraInfo.games[gameId];
  }
  saveExtraInfoToStorage();
  updateReminder("unsaved");
}

function downloadExtraInfo() {
  const blob = new Blob([JSON.stringify(extraInfo, null, 2)], {
    type: "application/json",
  });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = "extra_info.json";
  document.body.appendChild(a);
  a.click();
  a.remove();
  URL.revokeObjectURL(url);
  updateReminder("saved");
}

function loadExtraInfoObject(obj) {
  if (!obj || typeof obj !== "object" || !obj.games) return false;
  extraInfo = { games: obj.games };
  saveExtraInfoToStorage();
  updateReminder("loaded");
  applyFilters();
  return true;
}

function setUserData(gameId, patch) {
  const current = userData.games[gameId] || {};
  const next = { ...current, ...patch };
  const keep =
    Boolean(next.played) ||
    Boolean(next.owned) ||
    next.rating ||
    Boolean(next.favorite) ||
    Boolean(next.hidden) ||
    (next.notes && next.notes.length > 0);
  if (keep) {
    userData.games[gameId] = next;
  } else {
    delete userData.games[gameId];
  }
  saveUserDataToStorage();
  hasUnsavedChanges = true;
  updateReminder("unsaved");
}

function downloadUserData() {
  const blob = new Blob([JSON.stringify(userData, null, 2)], {
    type: "application/json",
  });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = "personalizacion.json";
  document.body.appendChild(a);
  a.click();
  a.remove();
  URL.revokeObjectURL(url);
  hasUnsavedChanges = false;
  updateReminder("saved");
}

function loadUserDataObject(obj) {
  if (!obj || typeof obj !== "object" || !obj.games) return false;
  userData = { games: obj.games };
  saveUserDataToStorage();
  hasUnsavedChanges = false;
  updateReminder("loaded");
  applyFilters();
  return true;
}

function loadData(data) {
  state.games = Array.isArray(data.games) ? data.games : [];
  state.filtered = state.games;
  const langs = new Set();
  const platforms = new Set();
  const blocks = new Set();
  const flags = new Set();
  const events = new Set();
  const tagsFacet = new Set();
  let withHistory = 0;
  state.games.forEach((g) => {
    collectFacet(langs, g.languages);
    collectFacet(platforms, g.platform);
    collectFacet(blocks, g.added_in_blocks);
    collectFacet(flags, g.flags);
    collectFacet(events, (g.history || []).map((h) => h.event));
    const extra = g.extra || {};
    collectFacet(tagsFacet, normalizeTags(extra.tags));
    if (g.history && g.history.length) withHistory += 1;
  });

  buildFacetChips(langsEl, Array.from(langs), (value, checked) => {
    if (checked) state.filters.languages.add(value);
    else state.filters.languages.delete(value);
    applyFilters();
  });
  buildFacetChips(platformsEl, Array.from(platforms), (value, checked) => {
    if (checked) state.filters.platforms.add(value);
    else state.filters.platforms.delete(value);
    applyFilters();
  });
  buildFacetChips(blocksEl, Array.from(blocks), (value, checked) => {
    if (checked) state.filters.blocks.add(value);
    else state.filters.blocks.delete(value);
    applyFilters();
  });
  buildFacetChips(flagsEl, Array.from(flags), (value, checked) => {
    if (checked) state.filters.flags.add(value);
    else state.filters.flags.delete(value);
    applyFilters();
  });
  buildFacetChips(tagsFilterEl, Array.from(tagsFacet), (value, checked) => {
    if (checked) state.filters.tags.add(value);
    else state.filters.tags.delete(value);
    applyFilters();
  });
  buildFacetChips(eventsEl, Array.from(events), (value, checked) => {
    if (checked) state.filters.events.add(value);
    else state.filters.events.delete(value);
    applyFilters();
  });

  statTotal.textContent = state.games.length;
  statVisible.textContent = state.games.length;
  statHistory.textContent = withHistory;
  loadingEl.textContent = "";
  bindInputs();
  renderList(state.games);
}

function enableLocalFileLoad(message) {
  loadingEl.textContent = message;
  fileBtn.classList.remove("hidden");
  userLoadBtn.classList.remove("hidden");
  userSaveBtn.classList.remove("hidden");
  extraLoadBtn.classList.remove("hidden");
  extraSaveBtn.classList.remove("hidden");
  fileBtn.addEventListener("click", () => fileInput.click());
  userLoadBtn.addEventListener("click", () => userFileInput.click());
  userSaveBtn.addEventListener("click", () => downloadUserData());
  extraLoadBtn.addEventListener("click", () => extraFileInput.click());
  extraSaveBtn.addEventListener("click", () => downloadExtraInfo());
  fileInput.addEventListener("change", (e) => {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => {
      try {
        const data = JSON.parse(reader.result);
        if (!data || typeof data !== "object" || !Array.isArray(data.games)) {
          loadingEl.textContent = t("ui.errors.invalidGames");
          return;
        }
        loadData(data);
        updateStatus(
          t("ui.status.showing", {
            visible: data.games.length,
            total: data.games.length,
          })
        );
      } catch (err) {
        loadingEl.textContent = t("ui.errors.invalidJson");
        console.error(err);
      }
    };
    reader.readAsText(file);
  });

  userFileInput.addEventListener("change", (e) => {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => {
      try {
        const data = JSON.parse(reader.result);
        if (!loadUserDataObject(data)) {
          loadingEl.textContent = t("ui.errors.invalidPersonal");
        }
      } catch (err) {
        loadingEl.textContent = t("ui.errors.invalidPersonal");
        console.error(err);
      }
    };
    reader.readAsText(file);
  });

  extraFileInput.addEventListener("change", (e) => {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => {
      try {
        const data = JSON.parse(reader.result);
        if (!loadExtraInfoObject(data)) {
          loadingEl.textContent = t("ui.errors.invalidExtra");
        }
      } catch (err) {
        loadingEl.textContent = t("ui.errors.invalidExtra");
        console.error(err);
      }
    };
    reader.readAsText(file);
  });
}

async function init() {
  buildOverlay();
  bindOverlayControls();

  document.querySelectorAll(".lang-btn").forEach((btn) => {
    btn.addEventListener("click", () => setLanguage(btn.dataset.lang));
  });
  const storedLang = localStorage.getItem(LANG_KEY);
  setLanguage(storedLang || "es");

  await loadUserDataFromStorage();
  await loadExtraInfoFromStorage();
  const multi = document.getElementById("lang-multi");
  multi.addEventListener("change", (e) => {
    state.filters.multiLang = e.target.checked;
    document.getElementById("lang-multi-chip").classList.toggle("active", e.target.checked);
    applyFilters();
  });
  const showHidden = document.getElementById("show-hidden");
  showHidden.addEventListener("change", (e) => {
    state.filters.showHidden = e.target.checked;
    document.getElementById("show-hidden-chip").classList.toggle("active", e.target.checked);
    applyFilters();
  });
  const showFavorites = document.getElementById("show-favorites");
  showFavorites.addEventListener("change", (e) => {
    state.filters.showFavorites = e.target.checked;
    document.getElementById("show-favorites-chip").classList.toggle("active", e.target.checked);
    applyFilters();
  });

  if (window.GAMES_DATA && Array.isArray(window.GAMES_DATA.games)) {
    userLoadBtn.classList.remove("hidden");
    userSaveBtn.classList.remove("hidden");
    extraLoadBtn.classList.remove("hidden");
    extraSaveBtn.classList.remove("hidden");
    userLoadBtn.addEventListener("click", () => userFileInput.click());
    userSaveBtn.addEventListener("click", () => downloadUserData());
    extraLoadBtn.addEventListener("click", () => extraFileInput.click());
    extraSaveBtn.addEventListener("click", () => downloadExtraInfo());
    userFileInput.addEventListener("change", (e) => {
      const file = e.target.files[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = () => {
        try {
          const obj = JSON.parse(reader.result);
          if (!loadUserDataObject(obj)) {
            loadingEl.textContent = t("ui.errors.invalidPersonal");
          }
        } catch (err) {
          loadingEl.textContent = t("ui.errors.invalidPersonal");
          console.error(err);
        }
      };
      reader.readAsText(file);
    });
    extraFileInput.addEventListener("change", (e) => {
      const file = e.target.files[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = () => {
        try {
          const obj = JSON.parse(reader.result);
          if (!loadExtraInfoObject(obj)) {
            loadingEl.textContent = t("ui.errors.invalidExtra");
          }
        } catch (err) {
          loadingEl.textContent = t("ui.errors.invalidExtra");
          console.error(err);
        }
      };
      reader.readAsText(file);
    });
    loadData(window.GAMES_DATA);
    updateStatus(
      t("ui.status.showing", {
        visible: window.GAMES_DATA.games.length,
        total: window.GAMES_DATA.games.length,
      })
    );
    return;
  }
  if (location.protocol === "file:") {
    enableLocalFileLoad(t("ui.errors.fileMode"));
    return;
  }

  const urls = ["../games.json", "games.json"];
  for (const url of urls) {
    try {
      const res = await fetch(url);
      if (!res.ok) continue;
      const data = await res.json();
      if (!data || typeof data !== "object" || !Array.isArray(data.games)) {
        continue;
      }
      userLoadBtn.classList.remove("hidden");
      userSaveBtn.classList.remove("hidden");
      extraLoadBtn.classList.remove("hidden");
      extraSaveBtn.classList.remove("hidden");
      userLoadBtn.addEventListener("click", () => userFileInput.click());
      userSaveBtn.addEventListener("click", () => downloadUserData());
      extraLoadBtn.addEventListener("click", () => extraFileInput.click());
      extraSaveBtn.addEventListener("click", () => downloadExtraInfo());
      userFileInput.addEventListener("change", (e) => {
        const file = e.target.files[0];
        if (!file) return;
        const reader = new FileReader();
        reader.onload = () => {
          try {
            const obj = JSON.parse(reader.result);
            if (!loadUserDataObject(obj)) {
              loadingEl.textContent = t("ui.errors.invalidPersonal");
            }
          } catch (err) {
            loadingEl.textContent = t("ui.errors.invalidPersonal");
            console.error(err);
          }
        };
        reader.readAsText(file);
      });
      extraFileInput.addEventListener("change", (e) => {
        const file = e.target.files[0];
        if (!file) return;
        const reader = new FileReader();
        reader.onload = () => {
          try {
            const obj = JSON.parse(reader.result);
            if (!loadExtraInfoObject(obj)) {
              loadingEl.textContent = t("ui.errors.invalidExtra");
            }
          } catch (err) {
            loadingEl.textContent = t("ui.errors.invalidExtra");
            console.error(err);
          }
        };
        reader.readAsText(file);
      });
      loadData(data);
      updateStatus(
        t("ui.status.showing", {
          visible: data.games.length,
          total: data.games.length,
        })
      );
      return;
    } catch (err) {
      // try next url
    }
  }
  enableLocalFileLoad(t("ui.errors.noGames"));
}

init().catch((err) => {
  loadingEl.textContent = t("ui.errors.invalidJson");
  console.error(err);
});
