const { fmt, ui, $ } = Admin;

// The user list rides along on the ordinary status poll; the inspection itself is only ever
// run on request, because it re-scores a whole pool.
Admin.onStatus((status) => {
  const select = $('inspector-user');
  if (!Admin.settled(select.parentElement)) return;
  const chosen = select.value;
  select.innerHTML = '<option value="">Choose a person…</option>' +
    (status.requestUsers || []).map((user) => '<option value="' + fmt.escape(user.id) + '">' +
      fmt.escape(user.username) + '</option>').join('');
  select.value = chosen;
});

function affinities(profile) {
  return [
    ['Genre', profile.genres], ['Studio', profile.studios], ['Actor', profile.actors],
    ['Director', profile.directors], ['Franchise', profile.franchises],
    ['Runtime', profile.runtimeRanges], ['Age rating', profile.ageRatings],
    ['Community rating', profile.communityRatings], ['Release period', profile.releasePeriods],
    ['Content type', profile.contentTypes],
  ].flatMap(([dimension, values]) => Object.entries(values || {}).map(([name, value]) => ({
    dimension, name, weight: value.weight || 0, evidence: value.evidence || 0,
  }))).sort((a, b) => Math.abs(b.weight) - Math.abs(a.weight));
}

function component(name, value) {
  return ui.chip(name + '=' + (value >= 0 ? '+' : '') + Number(value).toFixed(3),
    value < 0 ? 'bad' : undefined);
}

function resultCard(item, index) {
  const explanation = item.explanation || {};
  const components = Object.entries(explanation.components || {})
    .sort((a, b) => Math.abs(b[1]) - Math.abs(a[1]));
  const exposure = item.exposure || {};
  const facts = [item.type, item.year, item.runtimeMinutes ? item.runtimeMinutes + ' min' : null]
    .concat(item.genres || []).filter(Boolean).join(' · ');

  return '<section class="card"><div class="card-head split"><div>' +
    '<h2 class="card-title">#' + (index + 1) + ' · ' + fmt.escape(item.title) + '</h2>' +
    '<p class="card-note">' + fmt.escape(facts) + '</p></div>' +
    '<span class="score">' + Number(explanation.total || 0).toFixed(3) + '</span></div>' +
    '<p class="hint">' + fmt.escape(item.preparedReason || 'No legacy prepared explanation') +
      (item.compatibilityLabel ? ' · ' + fmt.escape(item.compatibilityLabel) : '') + '</p>' +
    '<div class="chips">' +
      (explanation.reasonCodes || []).map((code) => ui.chip(code, 'accent')).join('') +
      components.map(([name, value]) => component(name, value)).join('') + '</div>' +
    '<details><summary>Pool, row and exposure detail</summary><p class="hint">' +
      'Base rank ' + fmt.number(item.baseRank) +
      ' · base ' + Number(item.baseScore || 0).toFixed(3) +
      ' · affinity ' + Number(item.affinityScore || 0).toFixed(3) +
      ' · compatibility ' + Number(item.compatibilityScore || 0).toFixed(3) +
      ' · impressions ' + fmt.number(exposure.impressions) +
      ' · focuses ' + fmt.number(exposure.focuses) +
      ' · selects ' + fmt.number(exposure.selects) + '</p>' +
      '<div class="chips">' + (item.eligibleRows || [])
        .map((row) => ui.chip(row, 'accent')).join('') + '</div>' +
      (item.preparedEvidenceTitle
        ? '<p class="hint">Prepared evidence: ' + fmt.escape(item.preparedEvidenceTitle) + '</p>'
        : '') +
    '</details></section>';
}

function render(payload) {
  const meta = payload.profileMeta || {};
  const tiles = $('inspector-tiles');
  tiles.hidden = false;
  tiles.innerHTML = ui.tiles([
    ['prepared pool', fmt.number(payload.poolCandidates), { icon: 'database', tone: 'data' }],
    ['permission eligible', fmt.number(payload.permissionEligible), { icon: 'shield', tone: 'ok' }],
    ['ranked result', fmt.number((payload.items || []).length), { icon: 'sparkle', tone: 'note' }],
    ['source events', fmt.number(meta.sourceEvents), { icon: 'pulse', tone: 'info' }],
    ['algorithm', meta.algorithmVersion || '—', { small: true, icon: 'chip' }],
    ['pool built', fmt.when(meta.poolBuiltAt), { small: true, icon: 'clock' }],
  ]);

  const top = affinities(payload.profile || {}).slice(0, 24);
  const actions = payload.actions || [];
  $('inspector-profile-card').hidden = false;
  $('inspector-profile').innerHTML =
    '<div class="chips">' + (top.length
      ? top.map((entry) => ui.chip(entry.dimension + ': ' + entry.name + ' ' +
        (entry.weight >= 0 ? '+' : '') + entry.weight.toFixed(3) +
        ' · n=' + fmt.number(entry.evidence), entry.weight < 0 ? 'bad' : undefined)).join('')
      : ui.empty('No repeated affinity evidence yet; cold-start priors apply.')) + '</div>' +
    '<div class="chips">' + (actions.length
      ? actions.map((action) => ui.chip(action.action + ': ' +
        (action.title || action.itemId), 'accent')).join('')
      : ui.empty('No explicit recommendation actions.')) + '</div>';

  const items = payload.items || [];
  $('inspector-results').innerHTML = items.length
    ? items.map(resultCard).join('')
    : ui.empty('No candidates survived this context, the explicit exclusions and the permission filter.');
}

async function run() {
  const userId = $('inspector-user').value;
  if (!userId) {
    Admin.error('Choose a person to pressure-test.');
    return;
  }
  const params = new URLSearchParams({
    userId,
    context: $('inspector-context').value,
    minutes: $('inspector-minutes').value || '0',
    limit: '100',
  });
  const at = $('inspector-at').value;
  if (at) params.set('at', new Date(at).toISOString());
  $('inspector-hint').textContent = 'Running the permission check and the scorer…';
  try {
    render(await Admin.api('/admin/api/recommendations?' + params.toString()));
    $('inspector-hint').textContent = 'Scored at ' + new Date().toLocaleTimeString() + '.';
    Admin.error('');
  } catch (err) {
    $('inspector-hint').textContent = 'Pressure test failed.';
    Admin.error(err.message);
  }
}

Admin.ready(() => $('inspector-run').addEventListener('click', run));
