// RangerHQ Tuner — New Tab Page controller.
// Replaces Chrome's default new tab with the player as a focal landing.
// Same underlying mechanics as the popup (sources/index, messages to SW,
// chrome.storage state) — but laid out for a full viewport with a
// quick-station chip row and a searchable browse list.

import { TARGETS, TYPES } from '../lib/messages.js';
import { listAllStations, getSource } from '../sources/index.js';
import {
  getHistory, getFavourites,
  toggleFavourite, isFavourited,
  clearHistory, clearFavourites,
  searchUrls, formatRelativeTime,
  entrySignature,
} from '../lib/history.js';
import { THEME_KEY, THEME_DEFAULT, initTheme, applyTheme } from '../lib/theme.js';
import { QUICK_STATIONS_KEY, getQuickStations } from '../lib/quick-stations.js';

const els = {
  // Clock
  hm:          document.getElementById('nt-hm'),
  secs:        document.getElementById('nt-secs'),
  date:        document.getElementById('nt-date'),
  // Now playing
  station:     document.getElementById('nt-station'),
  track:       document.getElementById('nt-track'),
  state:       document.getElementById('nt-state'),
  // Controls
  play:        document.getElementById('nt-play'),
  volume:      document.getElementById('nt-volume'),
  // Stations
  quickList:   document.getElementById('nt-quick-list'),
  search:      document.getElementById('nt-search'),
  list:        document.getElementById('nt-station-list'),
  // Tabs + panes
  tabBar:        document.querySelector('.nt-tab-bar'),
  paneStations:  document.getElementById('nt-pane-stations'),
  paneHistory:   document.getElementById('nt-pane-history'),
  paneFavs:      document.getElementById('nt-pane-favourites'),
  historyList:   document.getElementById('nt-history-list'),
  favsList:      document.getElementById('nt-favs-list'),
  historyCount:  document.getElementById('nt-history-count'),
  favsCount:     document.getElementById('nt-favs-count'),
  historyClear:  document.getElementById('nt-history-clear'),
  favsClear:     document.getElementById('nt-favs-clear'),
  openOptions:   document.getElementById('nt-open-options'),
};

const STORAGE_KEYS = {
  stations:    'tuner.stationsCache',
  cachedAt:    'tuner.stationsCachedAt',
  currentId:   'tuner.currentStationId',
  volume:      'tuner.volume',
  isPlaying:   'tuner.isPlaying',
};

const CATALOGUE_TTL_MS = 6 * 60 * 60 * 1000;

// Quick-pick stations come from chrome.storage.local under tuner.quickStations,
// with DEFAULT_QUICK_IDS as fallback (8 curated for tidy 2-row layout).
// User can pick their own set in the Options page's "Quick Stations" card.
// See src/lib/quick-stations.js for the persistence helpers + defaults.
let quickIds = [];

let stations = [];
let currentStation = null;
let playing = false;
let lastQuery = '';
let clockTimer = null;

init().catch(err => {
  console.error('Newtab init failed:', err);
  setState('error');
  els.track.textContent = `Init failed: ${err.message}`;
});

async function init() {
  // Theme first — apply stored preference before anything paints to avoid
  // a brief dark flash if the user has picked light.
  await initTheme();

  // Clock — visible even before stations load.
  tickClock();
  clockTimer = setInterval(tickClock, 1000); // 1 Hz — live seconds

  const stored = await chrome.storage.local.get(Object.values(STORAGE_KEYS));
  if (typeof stored[STORAGE_KEYS.volume] === 'number') {
    els.volume.value = String(Math.round(stored[STORAGE_KEYS.volume] * 100));
  }
  playing = !!stored[STORAGE_KEYS.isPlaying];
  setState(playing ? 'playing' : 'idle');
  reflectPlayButton();

  const cached = stored[STORAGE_KEYS.stations];
  const cachedAt = stored[STORAGE_KEYS.cachedAt];
  const fresh = cached && cachedAt && (Date.now() - cachedAt < CATALOGUE_TTL_MS);

  if (fresh) {
    stations = cached;
  } else {
    stations = await listAllStations();
    await chrome.storage.local.set({
      [STORAGE_KEYS.stations]: stations,
      [STORAGE_KEYS.cachedAt]: Date.now(),
    });
  }

  const currentId = stored[STORAGE_KEYS.currentId];
  currentStation = stations.find(s => s.id === currentId) || null;
  reflectFirstRunHint();
  renderNow();
  quickIds = await getQuickStations();
  renderQuick();
  renderList();

  els.play.addEventListener('click', onPlayToggle);
  els.volume.addEventListener('input', onVolume);
  els.search.addEventListener('input', () => {
    lastQuery = els.search.value.trim().toLowerCase();
    renderList();
  });

  // Tabs
  els.tabBar.addEventListener('click', (e) => {
    const tab = e.target.closest('.nt-tab');
    if (!tab) return;
    if (tab.id === 'nt-open-options') {
      e.preventDefault();
      chrome.runtime.openOptionsPage();
      return;
    }
    selectTab(tab.dataset.tab);
  });
  els.historyClear.addEventListener('click', async () => {
    if (!confirm('Clear all track history? Favourites will be preserved.')) return;
    await clearHistory();
    await renderHistory();
  });
  els.favsClear.addEventListener('click', async () => {
    if (!confirm('Clear all favourites?')) return;
    await clearFavourites();
    await renderFavourites();
    await renderHistory(); // re-render history so star indicators update
  });

  // First render of track panes
  await renderHistory();
  await renderFavourites();

  // Deep-link via URL hash — popup's "History" / "Favourites" buttons
  // open us with #history or #favourites. Respect that on load AND on
  // hash change (e.g. user changes it manually).
  applyHashTab();
  window.addEventListener('hashchange', applyHashTab);

  chrome.runtime.onMessage.addListener((msg) => {
    if (msg.target !== TARGETS.POPUP) return; // SW broadcasts to "popup" as the UI bucket
    if (msg.type === TYPES.STATE_CHANGED) {
      setState(msg.state);
      playing = (msg.state === 'playing');
      reflectPlayButton();
      chrome.storage.local.set({ [STORAGE_KEYS.isPlaying]: playing });
    } else if (msg.type === TYPES.ERROR) {
      setState('error');
      els.track.textContent = `Stream error (code ${msg.code ?? '?'})`;
    } else if (msg.type === TYPES.METADATA_UPDATED) {
      if (msg.nowPlaying) els.track.textContent = formatNowPlaying(msg.nowPlaying);
    } else if (msg.type === TYPES.TRACK_LOGGED) {
      renderHistory();
    } else if (msg.type === TYPES.STORAGE_WIPED) {
      // Catastrophic reset — re-read everything from scratch.
      renderHistory();
      renderFavourites();
      renderQuick();
      renderList();
    }
  });

  // Cross-surface sync — if the popup (or another newtab) changes the
  // current station, re-render here live without a manual reload.
  chrome.storage.onChanged.addListener(async (changes, area) => {
    if (area !== 'local') return;

    if (changes[THEME_KEY]) {
      applyTheme(changes[THEME_KEY].newValue || THEME_DEFAULT);
    }

    if (changes[QUICK_STATIONS_KEY]) {
      quickIds = await getQuickStations();
      renderQuick();
    }

    if (changes[STORAGE_KEYS.currentId] && stations.length) {
      const newId = changes[STORAGE_KEYS.currentId].newValue;
      currentStation = stations.find(s => s.id === newId) || null;
      els.play.disabled = !currentStation;
      reflectFirstRunHint();
      renderNow();
      renderQuick();
      renderList();
    }

    if (changes[STORAGE_KEYS.isPlaying]) {
      playing = !!changes[STORAGE_KEYS.isPlaying].newValue;
      reflectPlayButton();
    }

    if (changes['tuner.history'])    renderHistory();
    if (changes['tuner.favourites']) {
      renderFavourites();
      renderHistory();
    }
  });

  els.play.disabled = !currentStation;
}

/* ---------- Clock ---------- */

function tickClock() {
  const now = new Date();
  const hh = String(now.getHours()).padStart(2, '0');
  const mm = String(now.getMinutes()).padStart(2, '0');
  const ss = String(now.getSeconds()).padStart(2, '0');
  els.hm.textContent = `${hh}:${mm}`;
  els.secs.textContent = ss;
  // Date only changes once a day; rendering the same string per tick is
  // free (no DOM diff cost) so we don't bother to short-circuit it.
  els.date.textContent = now.toLocaleDateString(undefined, {
    weekday: 'long', day: 'numeric', month: 'long', year: 'numeric',
  });
}

/* ---------- Rendering ---------- */

function renderNow() {
  if (currentStation) {
    els.station.textContent = currentStation.name;
    els.track.textContent = playing ? 'Now playing — fetching metadata…' : 'Ready to play';
  } else {
    els.station.textContent = 'Pick a station to begin';
    els.track.textContent = '—';
  }
}

function renderQuick() {
  els.quickList.innerHTML = '';
  if (!stations.length) {
    const empty = document.createElement('span');
    empty.className = 'nt-quick-empty';
    empty.textContent = 'Loading…';
    els.quickList.appendChild(empty);
    return;
  }
  if (!quickIds.length) {
    const empty = document.createElement('span');
    empty.className = 'nt-quick-empty';
    empty.textContent = 'No Quick Stations picked — set some in ⚙ Settings.';
    els.quickList.appendChild(empty);
    return;
  }
  for (const shortId of quickIds) {
    const fullId = `somafm:${shortId}`;
    const s = stations.find(st => st.id === fullId);
    if (!s) continue;
    const chip = document.createElement('button');
    chip.className = 'nt-quick-chip';
    if (currentStation && currentStation.id === s.id) chip.classList.add('is-active');
    chip.type = 'button';
    chip.textContent = s.name;
    chip.title = s.description || s.genre || s.name;
    chip.addEventListener('click', () => onPickStation(s));
    els.quickList.appendChild(chip);
  }
}

function renderList() {
  if (!stations.length) {
    els.list.innerHTML = '<li class="nt-quick-empty" style="padding:14px;">Loading stations…</li>';
    return;
  }
  const q = lastQuery;
  const filtered = q
    ? stations.filter(s =>
        s.name.toLowerCase().includes(q) ||
        s.genre.toLowerCase().includes(q) ||
        s.description.toLowerCase().includes(q))
    : stations;

  els.list.innerHTML = '';
  if (!filtered.length) {
    const li = document.createElement('li');
    li.className = 'nt-quick-empty';
    li.style.padding = '14px';
    li.textContent = 'No matches.';
    els.list.appendChild(li);
    return;
  }

  for (const s of filtered) {
    const li = document.createElement('li');
    li.className = 'nt-station-row';
    if (currentStation && currentStation.id === s.id) li.classList.add('is-active');
    li.setAttribute('role', 'option');

    const img = document.createElement('img');
    img.className = 'nt-station-art';
    img.alt = '';
    img.src = s.artworkUrl || '';
    img.onerror = () => { img.style.visibility = 'hidden'; };

    const meta = document.createElement('div');
    meta.className = 'nt-station-meta';

    const name = document.createElement('div');
    name.className = 'nt-station-name';
    name.textContent = s.name;

    const genre = document.createElement('div');
    genre.className = 'nt-station-genre';
    genre.textContent = s.genre || s.description.slice(0, 80);

    meta.appendChild(name);
    meta.appendChild(genre);
    li.appendChild(img);
    li.appendChild(meta);
    li.addEventListener('click', () => onPickStation(s));

    els.list.appendChild(li);
  }
}

/* ---------- Actions ---------- */

async function onPickStation(station) {
  currentStation = station;
  els.play.disabled = false;
  reflectFirstRunHint();
  await chrome.storage.local.set({ [STORAGE_KEYS.currentId]: station.id });
  renderNow();
  renderQuick();
  renderList();
  await playCurrent();
}

async function onPlayToggle() {
  if (!currentStation) return;
  if (playing) {
    await sendToSW({ type: TYPES.PAUSE });
  } else {
    await playCurrent();
  }
}

async function playCurrent() {
  if (!currentStation) return;
  setState('buffering');
  try {
    const source = getSource(currentStation.sourceId);
    if (!source) throw new Error(`Unknown source ${currentStation.sourceId}`);
    const streamUrl = await source.resolveStreamUrl(currentStation);
    await sendToSW({ type: TYPES.SET_VOLUME, volume: Number(els.volume.value) / 100 });
    const stationLite = {
      id:       currentStation.id,
      sourceId: currentStation.sourceId,
      name:     currentStation.name,
    };
    const resp = await sendToSW({ type: TYPES.PLAY, streamUrl, station: stationLite });
    if (!resp?.ok) throw new Error(resp?.error || 'PLAY failed');
    // Offscreen will broadcast METADATA_UPDATED within ~1s.
  } catch (err) {
    console.error(err);
    setState('error');
    els.track.textContent = `Couldn't play: ${err.message}`;
  }
}

async function onVolume() {
  const v = Number(els.volume.value) / 100;
  await chrome.storage.local.set({ [STORAGE_KEYS.volume]: v });
  await sendToSW({ type: TYPES.SET_VOLUME, volume: v });
}

/* ---------- Helpers ---------- */

// v0.4.0 first-run UX hint — body class drives the pulse + arrow CSS in
// newtab.css. Active when no station has been picked yet; cleared the
// moment one is selected (here or via cross-surface storage sync).
function reflectFirstRunHint() {
  document.body.classList.toggle('is-first-run', !currentStation);
}

function reflectPlayButton() {
  els.play.setAttribute('aria-pressed', playing ? 'true' : 'false');
  els.play.textContent = playing ? '❚❚ Pause' : '▶ Play';
}

function setState(state) {
  els.state.dataset.state = state;
  els.state.textContent = state;
}

function formatNowPlaying(np) {
  const left = np.artist ? `${np.artist} — ` : '';
  return `${left}${np.title}`;
}

function sendToSW(msg) {
  return chrome.runtime.sendMessage({ target: TARGETS.SW, ...msg });
}

/* ---------- Tabs ---------- */

function selectTab(name) {
  const map = {
    stations:   els.paneStations,
    history:    els.paneHistory,
    favourites: els.paneFavs,
  };
  for (const [k, pane] of Object.entries(map)) {
    const active = (k === name);
    pane.hidden = !active;
    const tabBtn = els.tabBar.querySelector(`[data-tab="${k}"]`);
    if (tabBtn) {
      tabBtn.classList.toggle('is-active', active);
      tabBtn.setAttribute('aria-selected', active ? 'true' : 'false');
    }
  }
}

function applyHashTab() {
  const hash = (location.hash || '').replace(/^#/, '').toLowerCase();
  if (hash === 'history' || hash === 'favourites' || hash === 'stations') {
    selectTab(hash);
  }
}

/* ---------- History + Favourites rendering ---------- */

async function renderHistory() {
  const entries = await getHistory();
  els.historyCount.textContent = `${entries.length} track${entries.length === 1 ? '' : 's'}`;
  if (!entries.length) {
    els.historyList.innerHTML = '<li class="nt-track-empty">No tracks logged yet. Play a station for a minute and they\'ll appear here.</li>';
    return;
  }
  // Newest first
  const favs = await getFavourites();
  const favSigs = new Set(favs.map(entrySignature));
  els.historyList.innerHTML = '';
  for (let i = entries.length - 1; i >= 0; i--) {
    els.historyList.appendChild(renderTrackRow(entries[i], favSigs));
  }
}

async function renderFavourites() {
  const entries = await getFavourites();
  els.favsCount.textContent = `${entries.length} favourite${entries.length === 1 ? '' : 's'}`;
  if (!entries.length) {
    els.favsList.innerHTML = '<li class="nt-track-empty">No favourites yet. Tap the star next to a history track to add it.</li>';
    return;
  }
  const favSigs = new Set(entries.map(entrySignature));
  els.favsList.innerHTML = '';
  for (let i = entries.length - 1; i >= 0; i--) {
    els.favsList.appendChild(renderTrackRow(entries[i], favSigs));
  }
}

function renderTrackRow(entry, favSigs) {
  const li = document.createElement('li');
  li.className = 'nt-track-row';

  // Main column — title (big), artist (accent), meta line
  const main = document.createElement('div');
  main.className = 'nt-track-main';

  const title = document.createElement('div');
  title.className = 'nt-track-title';
  title.textContent = entry.title;
  title.title = entry.title;

  const artist = document.createElement('div');
  artist.className = 'nt-track-artist';
  artist.textContent = entry.artist;
  artist.title = entry.artist;

  const meta = document.createElement('div');
  meta.className = 'nt-track-meta';
  const station = entry.station || '—';
  meta.textContent = `${station} · ${formatRelativeTime(entry.at)}`;

  main.append(title, artist, meta);

  // Right column — star + (future) overflow menu
  const controls = document.createElement('div');
  controls.className = 'nt-track-controls';

  const fav = document.createElement('button');
  fav.type = 'button';
  fav.className = 'nt-fav-btn';
  const isFav = favSigs.has(entrySignature(entry));
  fav.setAttribute('aria-pressed', isFav ? 'true' : 'false');
  fav.setAttribute('aria-label', isFav ? 'Remove from favourites' : 'Add to favourites');
  fav.textContent = isFav ? '★' : '☆';
  fav.addEventListener('click', async () => {
    const nowFav = await toggleFavourite(entry);
    fav.setAttribute('aria-pressed', nowFav ? 'true' : 'false');
    fav.textContent = nowFav ? '★' : '☆';
    fav.setAttribute('aria-label', nowFav ? 'Remove from favourites' : 'Add to favourites');
    // Re-render favs tab so it reflects the change.
    renderFavourites();
  });

  controls.appendChild(fav);

  // Search-link row — spans full width below
  const search = document.createElement('div');
  search.className = 'nt-search-row';
  const urls = searchUrls(entry.artist, entry.title);

  const linkOf = (svc, label) => {
    const a = document.createElement('a');
    a.className = `nt-search-link nt-search-link--${svc}`;
    a.href = urls[svc];
    a.target = '_blank';
    a.rel = 'noopener noreferrer';
    a.textContent = label;
    a.title = `Search ${label} for "${entry.artist} — ${entry.title}"`;
    return a;
  };
  search.append(
    linkOf('spotify',  'Spotify'),
    linkOf('youtube',  'YouTube'),
    linkOf('apple',    'Apple'),
    linkOf('bandcamp', 'Bandcamp'),
  );

  li.append(main, controls, search);
  return li;
}
