// Traffic-peak explanation — finds the spike days in a site's trend and works
// out WHAT drove each one: the top release/page that day (with its real title
// from the reports DB) and whether an email send landed that day. Powers the
// annotated daily chart, the "what drove the peaks" card and report sections.

import { timeseries, breakdown, resolveRange } from './ga4.js';
import { releaseTitles, topReleaseOfDay } from './legacy.js';
import { emailOverview } from './email.js';

// Friendly names for non-release pages, so a peak never shows a raw path.
const PATH_NAMES = {
  '': 'Homepage', '/': 'Homepage',
  '/releases': 'Releases index', '/news': 'News index',
  '/gallery': 'Photo gallery', '/gallery/photos': 'Photo gallery',
  '/media': 'Media library', '/models': 'Models index', '/search': 'Site search',
};
function humanPath(path) {
  const p = String(path || '').replace(/\/+$/, '') || '/';
  if (PATH_NAMES[p]) return PATH_NAMES[p];
  const seg = p.split('/').filter(Boolean).pop() || '';
  const words = seg.replace(/[-_]+/g, ' ').replace(/\.\w+$/, '').trim();
  if (!words) return 'Homepage';
  return words.replace(/\b\w/g, (c) => c.toUpperCase()) + ' page';
}

export async function trafficPeaks(siteId, rangeStr, maxPeaks = 15) {
  const range = resolveRange(rangeStr);
  const trend = await timeseries(siteId, range.startDate, range.endDate);
  if (!trend || trend.length < 5) return [];
  const vals = trend.map((t) => t.sessions || 0);
  const sorted = [...vals].sort((a, b) => a - b);
  const median = sorted[Math.floor(sorted.length / 2)] || 1;

  // EVERY "up" gets a label: a peak is the highest day of its hump — the
  // maximum within a ±2-day window — and at least as busy as the typical
  // (median) day. No ratio threshold: on a site with a weekly rhythm every
  // visible hump is labelled, exactly like a hand-annotated highlights chart.
  const isHumpTop = (i) => {
    const v = vals[i];
    if (v < median || v <= 0) return false;
    for (let k = Math.max(0, i - 2); k <= Math.min(vals.length - 1, i + 2); k++) {
      if (vals[k] > v) return false;
      if (vals[k] === v && k < i) return false; // plateau → label its first day only
    }
    return true;
  };
  const candidates = trend
    .map((t, i) => ({ ...t, i }))
    .filter(({ i }) => isHumpTop(i))
    .sort((a, b) => (b.sessions || 0) - (a.sessions || 0))
    .slice(0, maxPeaks); // busiest humps win when a long range has more than fit
  if (!candidates.length) return [];

  let sendDays = [];
  try {
    const em = await emailOverview(siteId, rangeStr);
    sendDays = (em?.sendDays || []).map((d) => d.date);
  } catch { /* email layer optional */ }

  // Explain each peak in parallel. Titles beat paths, always:
  //   1. a release URL among that day's top pages → its real title from the DB
  //   2. otherwise the reports DB's busiest release that day → its title
  //   3. otherwise a humanised page name ("Photo gallery", never "/gallery/photos")
  const peaks = await Promise.all(candidates.map(async (day) => {
    let label = null, kind = 'page', path = null, releaseId = null;
    try {
      const pages = await breakdown(siteId, 'pagePath', day.date, day.date, 12);
      const rel = (pages || []).find((p) => /(\d{3,})\/?$/.test(p.dimensionValue));
      const top = rel || (pages || []).find((p) => p.dimensionValue !== '/') || (pages || [])[0];
      if (top) {
        path = top.dimensionValue;
        const rid = /(\d{3,})\/?$/.exec(top.dimensionValue)?.[1];
        if (rid) {
          releaseId = rid;
          const t = await releaseTitles(siteId, [rid]).catch(() => ({}));
          if (t[rid]?.title) { label = t[rid].title; kind = 'release'; }
        }
      }
      if (!label) {
        // GA's top page wasn't a release — ask the reports DB what release
        // journalists were actually reading/downloading that day.
        const tr = await topReleaseOfDay(siteId, day.date).catch(() => null);
        if (tr?.title) { label = tr.title; kind = 'release'; releaseId = String(tr.id); }
      }
      if (!label && path) label = humanPath(path);
    } catch { /* leave label empty */ }
    const email = sendDays.includes(day.date);
    if (!label && email) { label = 'Email send day'; kind = 'email'; }
    return {
      date: day.date, sessions: day.sessions || 0,
      label: label || 'Traffic spike', kind: email && kind !== 'release' ? 'email' : kind,
      email, path, releaseId,
    };
  }));
  return peaks.sort((a, b) => a.date.localeCompare(b.date));
}
