// RangerHQ Tuner — Options page controller.
// Manages the user's local data: history cap, clear actions, stats display.
// Broadcasts STORAGE_WIPED after destructive actions so the popup/newtab
// rerender immediately.

import { TARGETS, TYPES } from '../lib/messages.js';
import {
  getHistory, getFavourites,
  getHistoryCap, setHistoryCap,
  clearHistory, clearFavourites, clearAll,
} from '../lib/history.js';
import { listAllStations } from '../sources/index.js';
import {
  THEME_KEY, THEME_DEFAULT, VALID_THEMES,
  getTheme, setTheme, applyTheme,
} from '../lib/theme.js';
import {
  QUICK_STATIONS_KEY, DEFAULT_QUICK_IDS,
  getQuickStations, setQuickStations, resetQuickStations,
} from '../lib/quick-stations.js';

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

const els = {
  version:        document.getElementById('opt-version'),
  statHistory:    document.getElementById('opt-stat-history'),
  statFavs:       document.getElementById('opt-stat-favs'),
  statCache:      document.getElementById('opt-stat-cache'),
  statBytes:      document.getElementById('opt-stat-bytes'),
  capSlider:      document.getElementById('opt-cap'),
  capValue:       document.getElementById('opt-cap-value'),
  clearHistory:   document.getElementById('opt-clear-history'),
  clearFavs:      document.getElementById('opt-clear-favs'),
  clearAll:       document.getElementById('opt-clear-all'),
  volumeDisplay:  document.getElementById('opt-volume-display'),
  lastStation:    document.getElementById('opt-last-station'),
  toast:          document.getElementById('opt-toast'),
  // Quick Stations picker (v0.5.3)
  qsCount:        document.getElementById('opt-qs-count'),
  qsList:         document.getElementById('opt-qs-list'),
  qsReset:        document.getElementById('opt-qs-reset'),
  // Export history (v0.5.3)
  exportJson:     document.getElementById('opt-export-json'),
  exportCsv:      document.getElementById('opt-export-csv'),
};

init().catch(err => {
  console.error('Options init failed:', err);
  toast(`Init failed: ${err.message}`, 'error');
});

async function init() {
  els.version.textContent = `v${chrome.runtime.getManifest().version}`;

  // Theme — apply stored preference + wire the radio group + listen for
  // cross-surface changes (popup/newtab pick a theme → Options reflects).
  await initThemeUI();

  // Quick Stations — render checkbox list of all SomaFM channels.
  await initQuickStationsUI();

  await refreshStats();
  await refreshPlayback();

  const cap = await getHistoryCap();
  els.capSlider.value = String(cap);
  els.capValue.textContent = String(cap);

  els.capSlider.addEventListener('input', () => {
    els.capValue.textContent = els.capSlider.value;
  });
  els.capSlider.addEventListener('change', async () => {
    const newCap = await setHistoryCap(Number(els.capSlider.value));
    els.capValue.textContent = String(newCap);
    toast(`History cap set to ${newCap}`);
    await refreshStats();
  });

  els.exportJson.addEventListener('click', () => exportHistoryAs('json'));
  els.exportCsv.addEventListener('click',  () => exportHistoryAs('csv'));

  els.clearHistory.addEventListener('click', async () => {
    if (!confirm('Clear all track history? Favourites will be preserved.')) return;
    await clearHistory();
    await refreshStats();
    notifyUis();
    toast('History cleared');
  });

  els.clearFavs.addEventListener('click', async () => {
    if (!confirm('Clear all favourites?')) return;
    await clearFavourites();
    await refreshStats();
    notifyUis();
    toast('Favourites cleared');
  });

  els.clearAll.addEventListener('click', async () => {
    if (!confirm('Wipe EVERYTHING — history, favourites, station cache, volume, last station? This cannot be undone.')) return;
    await clearAll();
    await refreshStats();
    await refreshPlayback();
    els.capSlider.value = '500';
    els.capValue.textContent = '500';
    notifyUis();
    toast('All local data wiped', 'danger');
  });

  // Refresh stats live if storage changes from another surface
  chrome.storage.onChanged.addListener((changes, area) => {
    if (area !== 'local') return;
    if (changes[THEME_KEY]) {
      const v = changes[THEME_KEY].newValue || THEME_DEFAULT;
      applyTheme(v);
      const radio = document.querySelector(`input[name="opt-theme"][value="${v}"]`);
      if (radio) radio.checked = true;
    }
    if (changes[QUICK_STATIONS_KEY]) {
      // Re-check the boxes to match the new picks (in case another surface
      // wiped storage or reset to defaults).
      const newPicks = new Set(await getQuickStations());
      for (const cb of document.querySelectorAll('#opt-qs-list input[type=checkbox]')) {
        cb.checked = newPicks.has(cb.value);
      }
      updateQuickStationsCount(newPicks.size);
    }
    if (Object.keys(changes).some(k => k.startsWith('tuner.'))) {
      refreshStats();
      refreshPlayback();
    }
  });
}

/** Apply the stored theme, check the matching radio, wire change handlers. */
async function initThemeUI() {
  const current = await getTheme();
  applyTheme(current);
  const checked = document.querySelector(`input[name="opt-theme"][value="${current}"]`);
  if (checked) checked.checked = true;
  for (const r of document.querySelectorAll('input[name="opt-theme"]')) {
    r.addEventListener('change', async (e) => {
      const v = e.target.value;
      if (!VALID_THEMES.includes(v)) return;
      await setTheme(v);
      applyTheme(v);
      toast(`Theme: ${v}`);
    });
  }
}

/** Render the Quick Stations picker — checkbox per SomaFM channel from
 *  the cached catalogue, with description below each name. Toggling a
 *  checkbox writes the current picks to chrome.storage.local under
 *  tuner.quickStations. NewTab's storage.onChanged listener re-renders
 *  the chip row instantly without a reload. */
async function initQuickStationsUI() {
  // Try the NewTab cache first (fast path).
  let cache = (await chrome.storage.local.get('tuner.stationsCache'))['tuner.stationsCache'] || [];

  // Fresh install fallback: if Options is opened BEFORE NewTab has ever
  // populated the cache, fetch SomaFM channels directly + cache them.
  if (!cache.length) {
    try {
      cache = await listAllStations();
      // Cache it back so future loads are instant.
      await chrome.storage.local.set({
        'tuner.stationsCache':  cache,
        'tuner.stationsCachedAt': Date.now(),
      });
    } catch (err) {
      console.warn('Quick Stations: failed to fetch channel list', err);
    }
  }

  // Stations come in as { id: "somafm:groovesalad", name, description, genre, ... }
  // Sort alphabetically by name for predictable picker order.
  const channels = [...cache].sort((a, b) => (a.name || '').localeCompare(b.name || ''));
  const picks = new Set(await getQuickStations());

  els.qsList.innerHTML = '';
  if (!channels.length) {
    const li = document.createElement('li');
    li.className = 'opt-qs-loading';
    li.textContent = 'Could not load SomaFM channel list — check your network connection and reopen Settings.';
    els.qsList.appendChild(li);
    return;
  }

  for (const ch of channels) {
    const shortId = (ch.id || '').split(':')[1];
    if (!shortId) continue;

    const li = document.createElement('li');
    li.className = 'opt-qs-row';

    const label = document.createElement('label');
    label.className = 'opt-qs-label';

    const cb = document.createElement('input');
    cb.type = 'checkbox';
    cb.value = shortId;
    cb.checked = picks.has(shortId);
    cb.addEventListener('change', onQuickStationToggle);

    const main = document.createElement('div');
    main.className = 'opt-qs-main';

    const name = document.createElement('div');
    name.className = 'opt-qs-name';
    name.textContent = ch.name || shortId;

    if (ch.genre) {
      const g = document.createElement('div');
      g.className = 'opt-qs-genre';
      g.textContent = ch.genre;
      main.appendChild(name);
      main.appendChild(g);
    } else {
      main.appendChild(name);
    }

    if (ch.description) {
      const desc = document.createElement('div');
      desc.className = 'opt-qs-desc';
      desc.textContent = ch.description;
      main.appendChild(desc);
    }

    label.appendChild(cb);
    label.appendChild(main);
    li.appendChild(label);
    els.qsList.appendChild(li);
  }

  updateQuickStationsCount(picks.size);

  els.qsReset.addEventListener('click', async () => {
    if (!confirm('Reset Quick Stations to the default 8?')) return;
    await resetQuickStations();
    // Re-check the boxes to match defaults
    const defaultSet = new Set(DEFAULT_QUICK_IDS);
    for (const cb of document.querySelectorAll('#opt-qs-list input[type=checkbox]')) {
      cb.checked = defaultSet.has(cb.value);
    }
    updateQuickStationsCount(defaultSet.size);
    toast('Quick Stations reset to defaults');
  });
}

async function onQuickStationToggle() {
  const all = [...document.querySelectorAll('#opt-qs-list input[type=checkbox]')];
  const picked = all.filter(c => c.checked).map(c => c.value);
  await setQuickStations(picked);
  updateQuickStationsCount(picked.length);
}

function updateQuickStationsCount(n) {
  els.qsCount.textContent = `${n} selected`;
}

async function refreshStats() {
  const [history, favs, all] = await Promise.all([
    getHistory(),
    getFavourites(),
    chrome.storage.local.get(null),
  ]);
  const cacheArr = Array.isArray(all[STORAGE_KEYS.stations]) ? all[STORAGE_KEYS.stations] : [];

  // Estimate "tuner.*" bytes — JSON stringification is close enough for
  // a user-facing display, no need for the exact storage-engine cost.
  let bytes = 0;
  for (const [k, v] of Object.entries(all)) {
    if (!k.startsWith('tuner.')) continue;
    try { bytes += k.length + JSON.stringify(v).length; } catch { /* nope */ }
  }

  els.statHistory.textContent = `${history.length} tracks`;
  els.statFavs.textContent    = `${favs.length} tracks`;
  els.statCache.textContent   = `${cacheArr.length} stations`;
  els.statBytes.textContent   = formatBytes(bytes);
}

async function refreshPlayback() {
  const stored = await chrome.storage.local.get([
    STORAGE_KEYS.volume,
    STORAGE_KEYS.currentId,
    STORAGE_KEYS.stations,
  ]);
  const vol = stored[STORAGE_KEYS.volume];
  els.volumeDisplay.textContent = typeof vol === 'number'
    ? `${Math.round(vol * 100)}%`
    : '—';

  const id = stored[STORAGE_KEYS.currentId];
  const cache = Array.isArray(stored[STORAGE_KEYS.stations]) ? stored[STORAGE_KEYS.stations] : [];
  const cur = cache.find(s => s.id === id);
  els.lastStation.textContent = cur?.name || (id || '—');
}

function notifyUis() {
  chrome.runtime.sendMessage({ target: TARGETS.POPUP, type: TYPES.STORAGE_WIPED })
    .catch(() => {}); // no listeners is fine
}

function formatBytes(n) {
  if (n < 1024)        return `${n} B`;
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
  return `${(n / (1024 * 1024)).toFixed(2)} MB`;
}

/** Download tuner.history as a JSON or CSV file. (v0.5.3) */
async function exportHistoryAs(format) {
  const history = await getHistory();
  if (!history.length) {
    toast('No history to export yet — play some music first', 'error');
    return;
  }

  let blob, ext, mime;
  if (format === 'json') {
    blob = new Blob([JSON.stringify(history, null, 2)], { type: 'application/json' });
    ext = 'json';
  } else {
    // CSV — RFC 4180 quoting (doubled quotes, every cell quoted for safety).
    const q = v => `"${String(v ?? '').replace(/"/g, '""')}"`;
    const header = 'artist,title,station,station_id,when_iso\n';
    const rows = history.map(e => [
      q(e.artist),
      q(e.title),
      q(e.station),
      q(e.stationId),
      q(e.at ? new Date(e.at).toISOString() : ''),
    ].join(',')).join('\n');
    blob = new Blob([header + rows + '\n'], { type: 'text/csv;charset=utf-8' });
    ext = 'csv';
  }

  const ts = new Date().toISOString().slice(0, 10);
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `rangerhq-tuner-history-${ts}.${ext}`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  setTimeout(() => URL.revokeObjectURL(url), 1000);

  toast(`Exported ${history.length} track${history.length === 1 ? '' : 's'} as ${ext.toUpperCase()}`);
}

function toast(message, tone) {
  els.toast.textContent = message;
  els.toast.dataset.tone = tone || '';
  els.toast.hidden = false;
  clearTimeout(toast._t);
  toast._t = setTimeout(() => { els.toast.hidden = true; }, 2400);
}
