/* @licstart  The following is the entire license notice for the JavaScript code in this file.
   Copyleft (🄯) 2025 James Osborne

   This file is free software: you can redistribute it and/or modify it under the terms of the GNU
   Affero General Public License as published by the Free Software Foundation, either version 3 of
   the License, or (at your option) any later version.

   This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
   even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License along with this program.
   If not, see <https://www.gnu.org/licenses/>. @licend  The above is the entire license notice for
   the JavaScript code in this file. */

(function () {
  'use strict';

  const CONFIG = {
    user: 'incentive.jb',
    smallLines: 7,
    lbBase: 'https://api.listenbrainz.org/1/',
    timeoutMs: 8000
  };
  const TOTAL = 1 + CONFIG.smallLines;

  const widget = document.getElementById('scrobble-widget');
  if (!widget) return;

  const urlJoin = (a, b) => a.replace(/\/+$/, '') + '/' + b.replace(/^\/+/, '');

  const HTML_ESC = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' };
  const esc = (s) => String(s ?? '').replace(/[&<>"]/g, (c) => HTML_ESC[c]);

  async function fetchListens() {
    const url = urlJoin(
      CONFIG.lbBase,
      `user/${encodeURIComponent(CONFIG.user)}/listens?count=${TOTAL}`
    );

    const ctrl = new AbortController();
    const timer = setTimeout(() => ctrl.abort(), CONFIG.timeoutMs);

    try {
      const res = await fetch(url, { cache: 'no-store', signal: ctrl.signal });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const j = await res.json();
      return (j && (j.payload?.listens || j.listens)) || [];
    } finally {
      clearTimeout(timer);
    }
  }

  fetchListens()
    .then((listens) => {
      if (!listens.length) {
        widget.textContent = 'No recent scrobbles.';
        return;
      }

      // Normalize to { artist, title } safely
      const rows = listens.map((l) => {
        const tm = l.track_metadata || l.track || {};
        const artist =
          tm.artist_name ?? tm.artist?.name ?? tm.artist ?? 'Unknown Artist';
        const title =
          tm.track_name ?? tm.title ?? tm.track?.title ?? tm.track ?? 'Unknown Track';
        return { artist: esc(artist), title: esc(title) };
      });

      const top = `
        <div class="scrobble-top scrobble-row">
          <span class="artist">${rows[0].artist}</span>
          <span class="title"><em>${rows[0].title}</em></span>
        </div>`;

      const more = rows
        .slice(1, 1 + CONFIG.smallLines)
        .map(
          (r) => `
        <li class="scrobble-row">
          <span class="artist">${r.artist}</span>
          <span class="title"><em>${r.title}</em></span>
        </li>`
        )
        .join('');

      widget.innerHTML = top + `<ul class="scrobble-more">${more}</ul>`;
    })
    .catch((err) => {
      console.warn('Scrobbles error:', err);
      widget.textContent = 'Scrobbles unavailable.';
    });
})();

