/* ============ Notion CMS integration ============ */

(function () {

// Set empty arrays immediately so the app doesn't crash before data loads
window.REVIEWS = [];
window.CONCERTS = [];

// Convert a Google Drive share link into a directly viewable image URL,
// same approach the original Squarespace-era hardcoded data used.
function driveFileId(url) {
  if (!url) return null;
  const m = url.match(/\/d\/([a-zA-Z0-9_-]+)/) || url.match(/[?&]id=([a-zA-Z0-9_-]+)/);
  return m ? m[1] : null;
}

function gd(id) {
  // Routed through our own proxy (api/image.js) instead of hotlinking
  // lh3.googleusercontent.com directly — see that file for why.
  // Bump this "v" whenever the edge cache needs busting — e.g. it was
  // caching Google's sign-in interstitial as a "valid" image before the
  // Drive sharing fix, and later a stale direct-serve-fallback response
  // from before the Blob store was fixed from Private to Public.
  return "/api/image?id=" + id + "&v=3";
}

function getImageUrl(page) {
  const driveUrl = page.properties['Image (.fi)']?.url;
  if (!driveUrl) return null;
  const id = driveFileId(driveUrl);
  return id ? gd(id) : driveUrl;
}

// Reads the "Spotify" column, whether it's a URL property (a plain link) or
// a text property (the whole <iframe> embed snippet pasted from Spotify's
// Share → Embed dialog), and returns a ready-to-use embed URL.
function getSpotifyEmbedUrl(page) {
  const prop = page.properties['Spotify'];
  if (!prop) return null;

  let raw = '';
  if (prop.type === 'url') raw = prop.url || '';
  else if (prop.type === 'rich_text') raw = (prop.rich_text || []).map(t => t.plain_text).join('');
  if (!raw) return null;

  const srcMatch = raw.match(/src=["']([^"']+)["']/);
  const url = (srcMatch ? srcMatch[1] : raw).trim();
  if (!url) return null;

  if (url.includes('/embed/')) return url.split('?')[0] + '?utm_source=generator';
  const m = url.match(/open\.spotify\.com\/(album|track|show|episode|playlist)\/([a-zA-Z0-9]+)/);
  return m ? `https://open.spotify.com/embed/${m[1]}/${m[2]}?utm_source=generator` : url;
}

function formatDate(dateStr) {
  if (!dateStr) return '';
  const [y, m, d] = dateStr.split('-');
  return `${parseInt(d)}.${parseInt(m)}.${y}`;
}

function parseTitle(fullTitle, type) {
  if (type === 'keikkaraportti') {
    // "Keikkaraportti: Artist / Venue, City Date"
    const rest = fullTitle.replace(/^Keikkaraportti:\s*/i, '');
    const slashIdx = rest.indexOf(' / ');
    if (slashIdx >= 0) {
      const artist = rest.slice(0, slashIdx).trim();
      const after = rest.slice(slashIdx + 3);
      const commaIdx = after.indexOf(', ');
      if (commaIdx >= 0) {
        const venue = after.slice(0, commaIdx).trim();
        const city = after.slice(commaIdx + 2).split(' ')[0];
        return { artist, venue, city };
      }
      return { artist, venue: after, city: '' };
    }
    return { artist: rest, venue: '', city: '' };
  } else {
    // "Levyarvio: Artist – Work" or "Klassikkoarvio: Artist – Work (1984)"
    // The separator isn't always the same dash character (en dash, em dash,
    // or a plain hyphen all show up depending on how the title was typed).
    const rest = fullTitle.replace(/^(?:Levyarvio|Klassikkoarvio):\s*/i, '');
    const dashMatch = rest.match(/\s[-–—]\s/);
    if (dashMatch) {
      const artist = rest.slice(0, dashMatch.index).trim();
      let work = rest.slice(dashMatch.index + dashMatch[0].length).trim();
      let year = null;
      const yearMatch = work.match(/\((\d{4})\)\s*$/);
      if (yearMatch) {
        year = parseInt(yearMatch[1]);
        work = work.replace(/\s*\(\d{4}\)\s*$/, '').trim();
      }
      return { artist, work, year };
    }
    return { artist: rest, work: '', year: null };
  }
}

function mapNotionPage(page) {
  const props = page.properties;
  const titleArr = props.Name?.title || [];
  const fullTitle = titleArr.map(t => t.plain_text).join('');

  const typeRaw = (props.Type?.multi_select?.[0]?.name || '').toLowerCase();
  const type = typeRaw === 'levyarvio' ? 'levyarvio'
    : typeRaw === 'klassikkoarvio' ? 'klassikkoarvio'
    : 'keikkaraportti';

  const dateStr = props.Date?.date?.start || '';
  const genre = props.Genre?.select?.name;
  const tags = genre ? [genre] : [];

  const cardImg = getImageUrl(page);
  const slug = page.id.replace(/-/g, '');
  const parsed = parseTitle(fullTitle, type);

  const base = {
    slug,
    notionPageId: page.id,
    type,
    title: fullTitle,
    date: dateStr,
    dateShort: formatDate(dateStr),
    cardImg,
    tags,
    body: null,
  };

  if (type === 'keikkaraportti') {
    // The title ends with the actual gig date (e.g. "...Helsinki 13.8.2022"),
    // which is what "Keikkapäivä" should show — not the Notion Date property,
    // which is when the report was posted.
    const concertDateMatch = fullTitle.match(/(\d{1,2}\.\d{1,2}\.\d{4})\s*$/);
    return {
      ...base,
      artist: parsed.artist || '',
      venue: parsed.venue || '',
      city: parsed.city || '',
      concertDate: concertDateMatch ? concertDateMatch[1] : formatDate(dateStr),
    };
  } else {
    const yearFromDate = dateStr ? parseInt(dateStr.slice(0, 4)) : null;
    return {
      ...base,
      artist: parsed.artist || '',
      work: parsed.work || '',
      year: parsed.year || yearFromDate,
      spotifyEmbedUrl: getSpotifyEmbedUrl(page),
    };
  }
}

async function fetchAllNotionPages() {
  let allPages = [];
  let cursor = null;
  do {
    const url = cursor ? `/api/notion?cursor=${encodeURIComponent(cursor)}` : '/api/notion';
    const res = await fetch(url);
    const data = await res.json();
    if (data.results) allPages = allPages.concat(data.results);
    cursor = data.has_more ? data.next_cursor : null;
  } while (cursor);
  return allPages;
}

// Fetch body blocks for a single Notion page and convert to paragraph array
window.fetchPostBody = async function (notionPageId) {
  const res = await fetch(`/api/notion?page_id=${notionPageId}`);
  const data = await res.json();
  const blocks = data.results || [];
  const paragraphs = [];

  for (const block of blocks) {
    const richText = block[block.type]?.rich_text;
    if (!richText || !richText.length) continue;
    const text = richText.map(t => {
      let s = t.plain_text || '';
      if (t.annotations?.bold && t.annotations?.italic) return `***${s}***`;
      if (t.annotations?.bold) return `**${s}**`;
      if (t.annotations?.italic) return `*${s}*`;
      return s;
    }).join('');
    // A single Notion block can itself contain multiple blank-line-separated
    // paragraphs (e.g. the whole article pasted in as one block) — split
    // those out so they render as separate paragraphs too.
    for (const chunk of text.split(/\n\s*\n+/)) {
      const trimmed = chunk.trim();
      if (trimmed) paragraphs.push(trimmed);
    }
  }

  return paragraphs;
};

// Load all posts from Notion on startup
fetchAllNotionPages().then(pages => {
  const reviews = [];
  const concerts = [];

  for (const page of pages) {
    const mapped = mapNotionPage(page);
    if (mapped.type === 'keikkaraportti') {
      concerts.push(mapped);
    } else {
      reviews.push(mapped);
    }
  }

  // API already returns sorted by date desc, but sort locally to be safe
  reviews.sort((a, b) => b.date.localeCompare(a.date));
  concerts.sort((a, b) => b.date.localeCompare(a.date));

  window.REVIEWS = reviews;
  window.CONCERTS = concerts;

  // Flag posts missing an Image (.fi) link instead of silently substituting
  // some other image — visible at #/debug/missing-images, and in the console.
  const missingImages = [...reviews, ...concerts].filter(p => !p.cardImg);
  window.MISSING_IMAGES = missingImages;
  if (missingImages.length) {
    console.warn(`Notion CMS: ${missingImages.length} post(s) missing an Image (.fi) link:`,
      missingImages.map(p => p.title));
  }

  window.dispatchEvent(new CustomEvent('notion-data-loaded'));
}).catch(err => {
  console.error('Failed to load Notion data:', err);
  window.dispatchEvent(new CustomEvent('notion-data-loaded'));
});

})();
