/**
 * Ragreen - Product module front-end logic
 * Requires config.js + jQuery loaded first.
 */

/* ---------- shared helpers ---------- */

/**
 * Renders a "← Prev  Page X of Y (N total)  Next →" control into
 * $container and wires the buttons to call onPageChange(newPage). Used
 * across every paginated list in the app so behavior/markup stays
 * consistent. Renders nothing if there's only one page.
 */
function renderPaginationControls(container, pagination, onPageChange) {
  const $el = $(container);
  if (!pagination || pagination.total_pages <= 1) {
    $el.html('');
    return;
  }
  $el.html(`
    <div style="display:flex; gap:10px; align-items:center; margin-top:16px;">
      <button class="mini-btn" id="pagPrevBtn" ${pagination.page <= 1 ? 'disabled' : ''}>← Prev</button>
      <span style="font-size:13px; color:var(--ink-muted);">Page ${pagination.page} of ${pagination.total_pages} (${pagination.total} total)</span>
      <button class="mini-btn" id="pagNextBtn" ${pagination.page >= pagination.total_pages ? 'disabled' : ''}>Next →</button>
    </div>
  `);
  $el.find('#pagPrevBtn').on('click', () => onPageChange(pagination.page - 1));
  $el.find('#pagNextBtn').on('click', () => onPageChange(pagination.page + 1));
}

function escapeHtml(str) {
  return $('<div>').text(str ?? '').html();
}

function formatDate(d) {
  if (!d) return '—';
  return new Date(d + 'T00:00:00').toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
}

function statusPillHtml(status) {
  const labels = { active: 'Active', inactive: 'Inactive', sold: 'Sold', expired: 'Expired' };
  return `<span class="status-pill status-${status}">${labels[status] || status}</span>`;
}

function starRatingHtml(avgRating, reviewCount) {
  if (!reviewCount) return '<span style="font-size:12px; color:var(--ink-muted);">No reviews yet</span>';
  const full = Math.round(avgRating);
  const stars = '★'.repeat(full) + '☆'.repeat(5 - full);
  return `<span style="font-size:13px; color:var(--amber); letter-spacing:1px;">${stars}</span> <span style="font-size:12px; color:var(--ink-muted);">${avgRating} (${reviewCount})</span>`;
}

function loadCategoriesInto($select) {
  return $.get(`${API_BASE}/categories/list.php`).done(function (res) {
    const options = res.data.categories.map(c => `<option value="${c.id}">${escapeHtml(c.name)}</option>`).join('');
    $select.append(options);
  });
}

/* ==========================================================
   PRODUCT FORM (add + edit, barcode scan, image picker)
   ========================================================== */

const ProductForm = {
  selectedFiles: [],       // File objects queued for upload
  existingImages: [],      // {id, url} already saved (edit mode)
  removedImageIds: [],
  editingId: null,
  html5QrCode: null,

  init() {
    const params = new URLSearchParams(window.location.search);
    this.editingId = params.get('id');
    const fromPantryId = params.get('from_pantry');

    const categoriesLoaded = loadCategoriesInto($('#category_id'));
    this.categoriesLoaded = categoriesLoaded;
    this.bindImagePicker();
    this.renderImagePicker(); // draws the "+" add button immediately - without this,
                              // a fresh "create" page has nothing to click at all,
                              // since this only otherwise gets called from
                              // loadExisting() (edit) or after a file is already
                              // chosen. Safe to call here unconditionally: with
                              // both existingImages and selectedFiles still empty
                              // at this point, it just renders the empty-state "+".
    this.bindBarcodeScan();
    this.bindSubmit();

    if (this.editingId) {
      $('#formTitle').text('Edit listing');
      $('#submitBtn').text('Save changes');
      // Wait for the category dropdown to be populated before selecting a
      // value in it - otherwise the option doesn't exist yet and the
      // category silently fails to show as selected (though it still
      // saves correctly - this is a display bug, not a save bug).
      categoriesLoaded.always(() => this.loadExisting(this.editingId));
    } else if (fromPantryId) {
      $('#formTitle').text('List this pantry item');
      $('#scanStatus').text('Prefilled from your pantry — add a price and photos, then publish.');
      this.loadFromPantry(fromPantryId);
    }
  },

  loadFromPantry(pantryId) {
    $.get(`${API_BASE}/pantry/get.php`, { id: pantryId })
      .done((res) => {
        const item = res.data.item;
        $('#name').val(item.product_name);
        $('#quantity').val(item.quantity);
        if (item.expiry_date) $('#expiry_date').val(item.expiry_date);
      })
      .fail(() => {
        showAlert($('#formAlert'), 'Could not load that pantry item — fill in the details manually.');
      });
  },

  loadExisting(id) {
    $.get(`${API_BASE}/products/get.php`, { id })
      .done((res) => {
        const p = res.data.product;
        $('#name').val(p.name);
        $('#category_id').val(p.category_id || '');
        $('#description').val(p.description);
        $('#quantity').val(p.quantity);
        $('#original_price').val(p.original_price || '');
        $('#selling_price').val(p.selling_price);
        $('#expiry_date').val(p.expiry_date || '');
        $('#location').val(p.location || '');
        $('#city').val(p.city || '');
        $('#country').val(p.country || '');
        $('#barcode').val(p.barcode || '');

        this.existingImages = (p.images || []).map((url, i) => ({ id: p.image_ids ? p.image_ids[i] : null, url }));
        this.renderImagePicker();
      })
      .fail(() => {
        showAlert($('#formAlert'), 'Could not load this listing.');
      });
  },

  bindBarcodeScan() {
    $('#startScanBtn').on('click', () => this.startCameraScan());
    $('#stopScanBtn').on('click', () => this.stopCameraScan());

    $('#lookupBarcodeBtn').on('click', () => {
      const barcode = valTrim('#barcode');
      if (!barcode) return;
      this.lookupBarcode(barcode);
    });
  },

  startCameraScan() {
    if (typeof Html5Qrcode === 'undefined') {
      $('#scanStatus').text('Camera scanner unavailable — enter the barcode manually below.');
      return;
    }
    $('#scanReader').addClass('active');
    $('#startScanBtn').hide();
    $('#stopScanBtn').show();

    this.html5QrCode = new Html5Qrcode('scanReader');
    this.html5QrCode.start(
      { facingMode: 'environment' },
      { fps: 10, qrbox: { width: 220, height: 120 } },
      (decodedText) => {
        $('#barcode').val(decodedText);
        this.stopCameraScan();
        this.lookupBarcode(decodedText);
      },
      () => {} // ignore per-frame scan failures
    ).catch(() => {
      $('#scanStatus').text('Could not access the camera — enter the barcode manually below.');
      this.stopCameraScan();
    });
  },

  stopCameraScan() {
    if (this.html5QrCode) {
      this.html5QrCode.stop().catch(() => {});
    }
    $('#scanReader').removeClass('active');
    $('#startScanBtn').show();
    $('#stopScanBtn').hide();
  },

  lookupBarcode(barcode) {
    $('#scanStatus').text('Looking up product…');
    $.get(`${API_BASE}/products/barcode_lookup.php`, { barcode })
      .done((res) => {
        if (res.data.found) {
          const p = res.data.product;
          const fromRagreen = res.data.source === 'ragreen';

          if (p.name && !$('#name').val()) $('#name').val(p.name);

          if (fromRagreen) {
            // Our own catalog also gives us a real category_id and description,
            // not just a name - reuse those too since they're directly usable,
            // unlike Open Food Facts' free-text category string.
            // Wait for the category dropdown to finish loading its options first -
            // otherwise .val() silently fails to select anything that doesn't
            // exist in the <select> yet (same race condition class fixed
            // earlier for the edit-listing flow).
            if (p.category_id) {
              $.when(this.categoriesLoaded).always(() => {
                if (!$('#category_id').val()) $('#category_id').val(p.category_id);
              });
            }
            if (p.description && !$('#description').val()) $('#description').val(p.description);
            $('#scanStatus').text(`Found in your Ragreen catalog: ${p.name || barcode}. Review the details below before publishing.`);
          } else {
            $('#scanStatus').text(`Found via Open Food Facts: ${p.name || barcode}. Review the details below before publishing.`);
          }
        } else {
          $('#scanStatus').text('No match found — please fill in the details manually.');
        }
      })
      .fail(() => $('#scanStatus').text('Lookup failed — please fill in the details manually.'));
  },

  bindImagePicker() {
    $('#imageInput').on('change', (e) => {
      const files = Array.from(e.target.files || []);
      this.selectedFiles = this.selectedFiles.concat(files).slice(0, 5 - this.existingImages.length);
      this.renderImagePicker();
      e.target.value = ''; // allow re-selecting the same file
    });
  },

  renderImagePicker() {
    const $picker = $('#imagePicker');
    $picker.empty();

    this.existingImages.forEach((img) => {
      $picker.append(`
        <div class="image-thumb" data-existing-id="${img.id}">
          <img src="${img.url}" alt="">
          <button type="button" class="remove-thumb" title="Remove">×</button>
        </div>
      `);
    });

    this.selectedFiles.forEach((file, idx) => {
      const url = URL.createObjectURL(file);
      $picker.append(`
        <div class="image-thumb" data-new-idx="${idx}">
          <img src="${url}" alt="">
          <button type="button" class="remove-thumb" title="Remove">×</button>
        </div>
      `);
    });

    const total = this.existingImages.length + this.selectedFiles.length;
    if (total < 5) {
      $picker.append(`<button type="button" class="image-add-btn" id="imageAddBtn">+</button>`);
      $('#imageAddBtn').on('click', () => $('#imageInput').trigger('click'));
    }

    $picker.find('.remove-thumb').on('click', function () {
      const $thumb = $(this).closest('.image-thumb');
      if ($thumb.data('existing-id')) {
        ProductForm.removedImageIds.push($thumb.data('existing-id'));
        ProductForm.existingImages = ProductForm.existingImages.filter(i => i.id !== $thumb.data('existing-id'));
      } else {
        ProductForm.selectedFiles.splice($thumb.data('new-idx'), 1);
      }
      ProductForm.renderImagePicker();
    });
  },

  bindSubmit() {
    $('#productForm').on('submit', (e) => {
      e.preventDefault();
      const $alert = $('#formAlert');
      hideAlert($alert);

      const sellingPrice = parseFloat($('#selling_price').val());
      if (!sellingPrice || sellingPrice <= 0) {
        showAlert($alert, 'Please enter a valid selling price.');
        return;
      }

      const formData = new FormData();
      if (this.editingId) formData.append('id', this.editingId);
      formData.append('name', valTrim('#name'));
      formData.append('category_id', $('#category_id').val());
      formData.append('description', valTrim('#description'));
      formData.append('quantity', $('#quantity').val() || 1);
      formData.append('original_price', $('#original_price').val());
      formData.append('selling_price', $('#selling_price').val());
      formData.append('expiry_date', $('#expiry_date').val());
      formData.append('location', valTrim('#location'));
      formData.append('city', valTrim('#city'));
      formData.append('country', valTrim('#country'));
      formData.append('barcode', valTrim('#barcode'));

      this.selectedFiles.forEach((file) => formData.append('images[]', file));
      this.removedImageIds.forEach((id) => formData.append('remove_image_ids[]', id));

      const url = this.editingId
        ? `${API_BASE}/products/update.php`
        : `${API_BASE}/products/create.php`;

      const $btn = $('#submitBtn');
      setLoading($btn, true, this.editingId ? 'Save changes' : 'Publish listing', 'Saving…');

      $.ajax({
        url, method: 'POST', data: formData, processData: false, contentType: false,
      })
        .done(() => {
          window.location.href = 'my-listings.html';
        })
        .fail((xhr) => {
          showAlert($alert, xhr.responseJSON?.message || 'Could not save the listing.');
        })
        .always(() => {
          setLoading($btn, false, this.editingId ? 'Save changes' : 'Publish listing', 'Saving…');
        });
    });
  },
};

/* ==========================================================
   MY LISTINGS
   ========================================================== */

const MyListings = {
  init() {
    this.load();
  },

  load(page = 1) {
    $.get(`${API_BASE}/products/list.php`, { scope: 'mine', page })
      .done((res) => this.render(res.data.products, res.data.pagination))
      .fail(() => {
        $('#listingsGrid').html('<div class="empty-state"><h3>Could not load your listings</h3></div>');
      });
  },

  render(products, pagination) {
    const $grid = $('#listingsGrid');
    if (!products.length) {
      $grid.html(`
        <div class="empty-state">
          <h3>No listings yet</h3>
          <p>Products you publish will show up here.</p>
          <a href="product-form.html" class="btn btn-primary" style="width:auto;padding:11px 20px;text-decoration:none;display:inline-flex;margin-top:12px;">Sell your first item</a>
        </div>
      `);
      renderPaginationControls('#listingsPagination', null);
      return;
    }

    $grid.html(products.map(p => `
      <div class="product-card" data-id="${p.id}">
        <div class="thumb">${p.thumbnail_url ? `<img src="${p.thumbnail_url}" alt="">` : `<img src="${PLACEHOLDER_IMG}" alt="Ragreen" class="no-photo">`}</div>
        <div class="body">
          ${statusPillHtml(p.status)}
          <div class="name">${escapeHtml(p.name)}</div>
          <div class="price">$${Number(p.selling_price).toFixed(2)}${p.original_price ? `<span class="original">$${Number(p.original_price).toFixed(2)}</span>` : ''}</div>
          <div class="meta">Expires ${formatDate(p.expiry_date)} · Qty ${p.quantity}</div>
        </div>
        <div class="card-actions">
          <button class="edit-btn">Edit</button>
          ${p.status === 'active'
            ? '<button class="deactivate-btn">Deactivate</button>'
            : (p.status === 'inactive' ? '<button class="activate-btn">Activate</button>' : '')}
          <button class="danger delete-btn">Delete</button>
        </div>
      </div>
    `).join(''));

    $grid.find('.edit-btn').on('click', function () {
      window.location.href = `product-form.html?id=${$(this).closest('.product-card').data('id')}`;
    });

    $grid.find('.deactivate-btn, .activate-btn').on('click', function () {
      const id = $(this).closest('.product-card').data('id');
      const status = $(this).hasClass('deactivate-btn') ? 'inactive' : 'active';
      const formData = new FormData();
      formData.append('id', id);
      formData.append('status', status);
      $.ajax({ url: `${API_BASE}/products/update.php`, method: 'POST', data: formData, processData: false, contentType: false })
        .done(() => MyListings.load());
    });

    $grid.find('.delete-btn').on('click', function () {
      const id = $(this).closest('.product-card').data('id');
      if (!confirm('Delete this listing permanently? This cannot be undone.')) return;
      $.ajax({
        url: `${API_BASE}/products/delete.php`, method: 'POST', contentType: 'application/json',
        data: JSON.stringify({ id }),
      }).done(() => MyListings.load());
    });

    renderPaginationControls('#listingsPagination', pagination, (p) => this.load(p));
  },
};

/* ==========================================================
   MARKETPLACE (public browse)
   ========================================================== */

const Marketplace = {
  userLat: null,
  userLng: null,

  init() {
    loadCategoriesInto($('#filterCategory'));
    this.loadLocations();
    this.bindFilters();
    this.load();
  },

  loadLocations() {
    $.get(`${API_BASE}/products/locations.php`).done((res) => {
      $('#filterCountry').append(res.data.countries.map(c => `<option value="${escapeHtml(c)}">${escapeHtml(c)}</option>`).join(''));
      $('#filterCity').append(res.data.cities.map(c => `<option value="${escapeHtml(c)}">${escapeHtml(c)}</option>`).join(''));
    });
  },

  bindFilters() {
    let debounce;
    $('#filterSearch').on('input', () => {
      clearTimeout(debounce);
      debounce = setTimeout(() => this.load(), 300);
    });
    $('#filterCategory, #filterSort, #filterMaxPrice, #filterCountry, #filterCity, #filterRadius').on('change', () => this.load());

    $('#nearMeBtn').on('click', () => {
      if (!navigator.geolocation) {
        alert('Your browser does not support location detection.');
        return;
      }
      const $btn = $('#nearMeBtn');
      setLoading($btn, true, '📍 Near me', 'Locating…');
      navigator.geolocation.getCurrentPosition(
        (pos) => {
          this.userLat = pos.coords.latitude;
          this.userLng = pos.coords.longitude;
          $('#filterRadius, #clearNearMeBtn').show();
          $('#sortDistanceOption').show();
          $('#filterSort').val('distance');
          setLoading($btn, false, '📍 Near me', 'Locating…');
          $btn.text('📍 Located');
          this.load();
        },
        () => {
          alert('Could not detect your location. You can still filter by country/city instead.');
          setLoading($btn, false, '📍 Near me', 'Locating…');
        }
      );
    });

    $('#clearNearMeBtn').on('click', () => {
      this.userLat = null;
      this.userLng = null;
      $('#filterRadius, #clearNearMeBtn, #sortDistanceOption').hide();
      $('#nearMeBtn').text('📍 Near me');
      if ($('#filterSort').val() === 'distance') $('#filterSort').val('newest');
      this.load();
    });
  },

  load(page = 1) {
    const params = {
      scope: 'marketplace',
      search: valTrim('#filterSearch'),
      category_id: $('#filterCategory').val(),
      sort: $('#filterSort').val(),
      max_price: $('#filterMaxPrice').val(),
      country: $('#filterCountry').val(),
      city: $('#filterCity').val(),
      page,
    };
    if (this.userLat !== null && this.userLng !== null) {
      params.near_lat = this.userLat;
      params.near_lng = this.userLng;
      params.max_distance_km = $('#filterRadius').val();
    }
    $.when(
      $.get(`${API_BASE}/products/list.php`, params),
      $.get(`${API_BASE}/ads/list.php`, { placement_key: 'marketplace_card' })
    ).done((productsRes, adsRes) => {
      this.render(productsRes[0].data.products, productsRes[0].data.pagination, adsRes[0].data.ads);
    });
  },

  render(products, pagination, cardAds = []) {
    const $gridA = $('#marketGridA');
    const $gridB = $('#marketGridB');
    $gridB.empty();

    if (!products.length) {
      const nearMeActive = this.userLat !== null;
      $gridA.html(`
        <div class="empty-state">
          <h3>No products match your search</h3>
          <p>${nearMeActive ? 'Try a wider radius, or clear "Near me" to see everything.' : 'Try adjusting the filters above.'}</p>
        </div>
      `);
      $('#marketInfeedAd').empty();
      renderPaginationControls('#marketPagination', null);
      return;
    }

    const cardHtml = (p) => `
      <a class="product-card ${p.is_currently_featured ? 'featured-card' : ''}" href="product-details.html?id=${p.id}" style="text-decoration:none;color:inherit;">
        <div class="thumb">
          ${p.is_currently_featured ? '<span class="featured-badge">★ Featured</span>' : ''}
          ${p.thumbnail_url ? `<img src="${p.thumbnail_url}" alt="">` : `<img src="${PLACEHOLDER_IMG}" alt="Ragreen" class="no-photo">`}
        </div>
        <div class="body">
          <div class="name">${escapeHtml(p.name)}</div>
          <div class="price">$${Number(p.selling_price).toFixed(2)}${p.original_price ? `<span class="original">$${Number(p.original_price).toFixed(2)}</span>` : ''}</div>
          <div style="margin:2px 0;">${starRatingHtml(p.avg_rating, p.review_count)}</div>
          <div class="meta">${p.category_name ? escapeHtml(p.category_name) + ' · ' : ''}Expires ${formatDate(p.expiry_date)}${p.distance_km !== undefined && p.distance_km !== null ? ` · ${p.distance_km} km away` : ''}</div>
        </div>
      </a>
    `;

    // Listing-card ads: a completely different mechanism from the banner
    // ad above - these are ordinary grid items, the exact same shape and
    // size as a product card, so they carry NONE of the full-span
    // row-break risk. They're simply spliced into the same sequence as
    // real products before anything is split into Grid A/B, and flow
    // naturally like any other card. Multiple configured ads cycle in
    // rotation as they're inserted, one every CARD_AD_INTERVAL products.
    const adCardHtml = (ad) => `
      <a class="product-card ad-card" href="${typeof escapeAttr === 'function' ? escapeAttr(ad.custom_link_url) : ad.custom_link_url}" target="_blank" rel="noopener sponsored" style="text-decoration:none;color:inherit;">
        <div class="thumb">
          <span class="featured-badge" style="background:var(--ink-muted);">Sponsored</span>
          <img src="${ad.custom_image_url}" alt="${escapeHtml(ad.custom_alt_text || 'Advertisement')}">
        </div>
        <div class="body">
          <div class="name">${escapeHtml(ad.custom_alt_text || 'Advertisement')}</div>
        </div>
      </a>
    `;

    const CARD_AD_INTERVAL = 8;
    const sequence = [];
    let adCycle = 0;
    products.forEach((p, i) => {
      sequence.push({ html: cardHtml(p) });
      if (cardAds.length && (i + 1) % CARD_AD_INTERVAL === 0) {
        sequence.push({ html: adCardHtml(cardAds[adCycle % cardAds.length]) });
        adCycle++;
      }
    });

    // In-feed ad: rendered as a genuine sibling BETWEEN two separate grid
    // containers, never as an item inside either one. A card spliced
    // directly into a single CSS grid with "grid-column: 1/-1" forces a
    // line break wherever it lands, stranding whatever's left of the
    // current row - true whether or not the slot actually has an ad in
    // it, so this isn't something a "skip if empty" check alone can fix.
    // Splitting into two independent grids sidesteps the problem
    // entirely: the ad slot (and #marketGridWrap's flex gap) supplies the
    // spacing, and neither grid ever contains anything but cards.
    // 12, not 6 - this grid caps at 4 columns (1080px container / 220px+
    // minimum card width), and 12 is evenly divisible by every column
    // count this design can ever produce (1, 2, 3, or 4), so Grid A always
    // ends on a complete row. The split point is applied to the combined
    // sequence (products + interspersed card-ads), so this guarantee
    // holds however many card-ads got mixed in too.
    const splitIndex = Math.min(12, sequence.length);
    $gridA.html(sequence.slice(0, splitIndex).map(s => s.html).join(''));
    $gridB.html(sequence.slice(splitIndex).map(s => s.html).join(''));

    if (typeof renderAdSlot === 'function') {
      renderAdSlot('marketplace_infeed', '#marketInfeedAd');
    }

    renderPaginationControls('#marketPagination', pagination, (p) => this.load(p));
  },
};
