/* ============================================================
   AliothPress — unified media picker
   ------------------------------------------------------------
   One shared CORE (fetching /admin/media/api/list with live
   search + pagination + selection) and thin SKINS on top:

     window.openImagePicker(callback)          — admin modal skin
     window.pickImageFor(inputId, altInputId)  — convenience wrapper
     window.AdminImagePicker.attach(cfg)       — core for other
        surfaces (Page Builder passes its own pb-* class markup)

   All visuals come from EXISTING stylesheet classes of the host
   surface (admin.css: filter-bar / pagination / image-picker-*;
   builder: pb-*). No inline colors — dark & light themes keep
   working through the hosts' CSS variables.

   Translations are passed via window.AdminPickerI18n (admin) or
   through cfg.i18n (builder) so this file stays free of Jinja.
   ============================================================ */
(function () {
  'use strict';

  var DEFAULT_I18N = {
    select_image: 'Select image',
    loading: 'Loading…',
    no_images_picker: 'No images in the library yet.',
    upload_first_link: 'Upload your first image',
    error_loading_retry: 'Could not load images. Please try again.',
    search_placeholder: 'Search images…',
    no_results: 'No images match your search',
    prev: '← Prev',
    next: 'Next →'
  };

  function t(key) {
    var i18n = window.AdminPickerI18n || {};
    return i18n[key] || DEFAULT_I18N[key] || key;
  }

  function escapeHtml(s) {
    if (s == null) return '';
    return String(s)
      .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
  }

  /* ── Shared core ─────────────────────────────────────────────
     cfg = {
       searchInput   input element (styled by the host surface)
       gridEl        container that receives the item cards
       pagerEl       container that receives pagination controls
       perPage       page size (default 20)
       multiple      allow multi-selection (default false)
       renderItem(img, idx)      -> card HTML; root MUST carry
                                    data-pick-idx="<idx>"
       renderLoading()           -> HTML while a request runs
       renderEmpty(hasQuery)     -> HTML for zero results
       renderError()             -> HTML for a failed request
       pager: {
         nav(page, label)        -> clickable control; MUST carry
                                    data-pick-page="<page>"
         current(page)           -> non-clickable current page
         gap()                   -> '…' filler
       }
       markSelected(cardEl, on)  (multiple) toggle selected look
       onPick(img)               (single)   an image was chosen
       onToggle(img, on, count)  (multiple) selection changed
     }
     Returns { reload, getSelected }.                            */
  function attachPickerCore(cfg) {
    var perPage = cfg.perPage || 20;
    var state = { q: '', page: 1, items: [], selected: {} };
    var debounceTimer;

    /* Host surfaces without window.AdminPickerI18n (the Page Builder)
       pass their strings via cfg.i18n. */
    function tt(key) {
      return (cfg.i18n && cfg.i18n[key]) || t(key);
    }

    function buildPager(data) {
      if (!cfg.pagerEl) return;
      if (!data || data.pages <= 1) { cfg.pagerEl.innerHTML = ''; return; }
      var html = '';
      if (data.has_prev) html += cfg.pager.nav(data.prev_num, tt('prev'));
      /* Same window algorithm as the media library's live search:
         first, last, and current ±2 with gap fillers. */
      var nums = [], i;
      for (i = 1; i <= data.pages; i++) {
        if (i === 1 || i === data.pages || (i >= data.page - 2 && i <= data.page + 2)) {
          nums.push(i);
        } else if (nums[nums.length - 1] !== null) {
          nums.push(null);
        }
      }
      nums.forEach(function (p) {
        if (p === null) html += cfg.pager.gap();
        else if (p === data.page) html += cfg.pager.current(p);
        else html += cfg.pager.nav(p, String(p));
      });
      if (data.has_next) html += cfg.pager.nav(data.next_num, tt('next'));
      cfg.pagerEl.innerHTML = html;
    }

    function applySelection() {
      if (!cfg.multiple || !cfg.markSelected) return;
      var cards = cfg.gridEl.querySelectorAll('[data-pick-idx]');
      cards.forEach(function (card) {
        var img = state.items[parseInt(card.getAttribute('data-pick-idx'), 10)];
        cfg.markSelected(card, !!(img && state.selected[img.url]));
      });
    }

    function render(data) {
      state.items = data.items || [];
      if (!state.items.length) {
        cfg.gridEl.innerHTML = cfg.renderEmpty(!!state.q);
        buildPager(null);
        return;
      }
      cfg.gridEl.innerHTML = state.items.map(function (img, idx) {
        return cfg.renderItem(img, idx);
      }).join('');
      applySelection();
      buildPager(data);
    }

    function load() {
      cfg.gridEl.innerHTML = cfg.renderLoading();
      if (cfg.pagerEl) cfg.pagerEl.innerHTML = '';
      var params = new URLSearchParams();
      params.set('page', state.page);
      params.set('per_page', perPage);
      if (state.q) params.set('q', state.q);
      fetch('/admin/media/api/list?' + params.toString(), { credentials: 'same-origin' })
        .then(function (res) { return res.json(); })
        .then(render)
        .catch(function (err) {
          console.error('Error loading images:', err);
          cfg.gridEl.innerHTML = cfg.renderError();
        });
    }

    cfg.gridEl.addEventListener('click', function (e) {
      var card = e.target.closest('[data-pick-idx]');
      if (!card || !cfg.gridEl.contains(card)) return;
      var img = state.items[parseInt(card.getAttribute('data-pick-idx'), 10)];
      if (!img) return;
      if (cfg.multiple) {
        if (state.selected[img.url]) delete state.selected[img.url];
        else state.selected[img.url] = img;
        if (cfg.markSelected) cfg.markSelected(card, !!state.selected[img.url]);
        if (cfg.onToggle) cfg.onToggle(img, !!state.selected[img.url],
                                       Object.keys(state.selected).length);
      } else if (cfg.onPick) {
        cfg.onPick(img);
      }
    });

    if (cfg.pagerEl) {
      cfg.pagerEl.addEventListener('click', function (e) {
        var ctl = e.target.closest('[data-pick-page]');
        if (!ctl) return;
        e.preventDefault();
        state.page = parseInt(ctl.getAttribute('data-pick-page'), 10) || 1;
        load();
      });
    }

    if (cfg.searchInput) {
      cfg.searchInput.addEventListener('input', function () {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(function () {
          state.q = cfg.searchInput.value.trim();
          state.page = 1;
          load();
        }, 300);
      });
    }

    load();

    return {
      reload: load,
      getSelected: function () {
        return Object.keys(state.selected).map(function (url) {
          return state.selected[url];
        });
      }
    };
  }

  /* ── Admin modal skin ────────────────────────────────────────
     Markup mirrors the Media Library page one-to-one: the same
     .filter-bar search input on top and the same .pagination
     controls at the bottom, so the picker looks identical to
     every other admin search. */
  function openImagePicker(callback) {
    var modal = document.createElement('div');
    modal.className = 'image-picker-overlay';
    var mc = document.createElement('div');
    mc.className = 'image-picker-modal';
    mc.innerHTML =
      '<div class="image-picker-header">' +
        '<h2>' + escapeHtml(t('select_image')) + '</h2>' +
        '<button type="button" class="image-picker-close" aria-label="Close">&times;</button>' +
      '</div>' +
      '<div class="filter-bar">' +
        '<input type="text" placeholder="🔍 ' + escapeHtml(t('search_placeholder')) + '" autocomplete="off">' +
      '</div>' +
      '<div class="image-picker-grid"></div>' +
      '<div class="pagination"></div>';
    modal.appendChild(mc);
    document.body.appendChild(modal);

    function close() {
      if (modal && modal.parentNode) modal.parentNode.removeChild(modal);
      document.removeEventListener('keydown', onKey);
    }
    function onKey(e) { if (e.key === 'Escape') close(); }
    mc.querySelector('.image-picker-close').addEventListener('click', close);
    modal.addEventListener('click', function (e) { if (e.target === modal) close(); });
    document.addEventListener('keydown', onKey);

    attachPickerCore({
      searchInput: mc.querySelector('.filter-bar input'),
      gridEl: mc.querySelector('.image-picker-grid'),
      pagerEl: mc.querySelector('.pagination'),
      renderLoading: function () {
        return '<div class="image-picker-loading">' + escapeHtml(t('loading')) + '</div>';
      },
      renderEmpty: function (hasQuery) {
        if (hasQuery) {
          return '<div class="image-picker-empty"><p>' +
                 escapeHtml(t('no_results')) + '</p></div>';
        }
        return '<div class="image-picker-empty">' +
               '<p>' + escapeHtml(t('no_images_picker')) + '</p>' +
               '<a href="/admin/media/upload" target="_blank">' +
               escapeHtml(t('upload_first_link')) + '</a></div>';
      },
      renderError: function () {
        return '<div class="image-picker-error">' + escapeHtml(t('error_loading_retry')) + '</div>';
      },
      renderItem: function (img, idx) {
        var url = img.url || '';
        var thumb = img.thumb || url;
        var alt = img.alt_text || '';
        /* People search the library by the file name they uploaded, not by
           the exact alt text — show it under the alt line. Prefer the
           ORIGINAL upload name (that's the one they remember); fall back to
           the stored name for legacy rows. Skip the line when it would just
           repeat the alt text. */
        var fname = img.original_filename || img.filename || '';
        var w = (img.width != null) ? img.width : '?';
        var h = (img.height != null) ? img.height : '?';
        return '<div class="image-picker-item" data-pick-idx="' + idx + '">' +
          '<img src="' + escapeHtml(thumb) + '" alt="' + escapeHtml(alt) + '" loading="lazy">' +
          '<div class="image-picker-item__alt">' + escapeHtml(alt) + '</div>' +
          (fname && fname !== alt
            ? '<div class="image-picker-item__filename" title="' + escapeHtml(fname) + '">' + escapeHtml(fname) + '</div>'
            : '') +
          '<div class="image-picker-item__size">' + escapeHtml(w + ' × ' + h + ' px') + '</div>' +
          '</div>';
      },
      pager: {
        nav: function (page, label) {
          return '<a href="#" data-pick-page="' + page + '">' + escapeHtml(label) + '</a>';
        },
        current: function (page) { return '<span class="current">' + page + '</span>'; },
        gap: function () { return '<span>…</span>'; }
      },
      onPick: function (img) {
        try { callback(img.url || '', { alt: img.alt_text || '' }); } finally { close(); }
      }
    });
  }

  function pickImageFor(inputId, altInputId) {
    openImagePicker(function (url, data) {
      var input = document.getElementById(inputId);
      if (input) input.value = url;
      if (altInputId && data && data.alt) {
        var altField = document.getElementById(altInputId);
        if (altField && !altField.value) altField.value = data.alt;
      }
      var preview = document.getElementById(inputId + '_preview');
      var wrap = document.getElementById(inputId + '_preview_wrap');
      if (preview) preview.src = url;
      if (wrap) wrap.style.display = 'block';
      if (typeof window.updateImageHints === 'function') window.updateImageHints();
    });
  }

  window.openImagePicker = openImagePicker;
  window.pickImageFor = pickImageFor;
  window.AdminImagePicker = { attach: attachPickerCore, escapeHtml: escapeHtml };
})();
