/**
 * Inject xAPI scripts and optional instrumentation into HTML
 * Uses regex-based approach for Node.js compatibility
 */

export function injectScriptsIntoHtml(htmlContent, options = {}) {
  const { mode = 'minimal', keepAnalytics = false, aiScript = null } = options;

  try {
    let html = htmlContent;

    // Remove CSP-blocked scripts unless keepAnalytics is true
    if (!keepAnalytics) {
      html = removeBlockedScripts(html);
    }

    // Inject xAPI libraries
    html = injectXapiLibs(html);

    // Inject base-mode instrumentation first
    switch (mode) {
      case 'timeline':
        html = injectTimelineTracking(html);
        break;
      case 'quiz':
        html = injectQuizTracking(html);
        break;
      case 'minimal':
      default:
        // Just xAPI libs, no tracking
        break;
    }

    return html;
  } catch (error) {
    console.error('Error injecting scripts:', error);
    return htmlContent;
  }
}

export function injectAIAgentScriptIntoHtml(htmlContent, aiScript) {
  if (!aiScript) {
    return htmlContent;
  }
  return injectAIAgentScript(htmlContent, aiScript);
}

function removeBlockedScripts(html) {
  const patterns = [
    /<script[^>]*src=['"](.*?google-analytics.*?)['"][^>]*><\/script>/gi,
    /<script[^>]*src=['"](.*?ga\.js.*?)['"][^>]*><\/script>/gi,
    /<script[^>]*src=['"](.*?analytics\.js.*?)['"][^>]*><\/script>/gi,
    /<script[^>]*src=['"](.*?pagead.*?)['"][^>]*><\/script>/gi,
    /<script[^>]*src=['"](.*?doubleclick.*?)['"][^>]*><\/script>/gi
  ];

  let result = html;
  patterns.forEach(pattern => {
    result = result.replace(pattern, '');
  });

  return result;
}

function injectXapiLibs(html) {
  if (html.includes('xapiwrapper') && html.includes('xAPI.js')) {
    return html;
  }

  const xapiLibsScript = `
    <script src="./lib/xapiwrapper.min.js"></script>
    <script src="./lib/xAPI.js"></script>`;

  if (html.includes('</head>')) {
    return html.replace('</head>', xapiLibsScript + '\n  </head>');
  }

  return html + xapiLibsScript;
}

function injectTimelineTracking(html) {
  const script = `
    <!-- xAPI Timeline Tracking -->
    <script>
(function() {
  // IMPORTANT:
  // - Do not send statements directly with fetch/XHR (can trigger CORS failures)
  // - Use the existing injected SLS glue: window.storeState(...) (vendor/xAPI.js)
  const xapiState = { startTime: Date.now(), actions: [] };
  const flushState = { timer: null, lastSentAt: 0, lastSig: '' };

  function safeStore(payload) {
    try {
      if (typeof window.storeState === 'function') {
        window.storeState(payload);
      }
    } catch (e) {
      // never break content
    }
  }

  function summarizeTarget(t) {
    try {
      return {
        tag: (t && t.tagName) ? String(t.tagName).toLowerCase() : null,
        id: t && t.id ? String(t.id).substring(0, 120) : null,
        name: t && t.name ? String(t.name).substring(0, 120) : null,
        value: t && t.value != null ? String(t.value).substring(0, 120) : null,
        text: t && t.textContent ? String(t.textContent).trim().substring(0, 120) : null
      };
    } catch {
      return {};
    }
  }

  function logAction(type, data) {
    xapiState.actions.push({ t: Date.now(), type, data: data || null });
    // keep payload small
    if (xapiState.actions.length > 120) xapiState.actions.shift();
  }

  function captureDashboardSnapshot() {
    // Best-effort: capture visible "dashboard"/"results" info without knowing the content structure.
    try {
      const selectors = [
        '[id*="dashboard" i]', '[class*="dashboard" i]',
        '[id*="result" i]', '[class*="result" i]',
        '[id*="score" i]', '[class*="score" i]'
      ];
      const el = document.querySelector(selectors.join(','));
      if (!el) return null;
      const text = (el.textContent || '').trim().replace(/\\s+/g, ' ');
      if (!text) return null;
      return { selector: el.id ? ('#' + el.id) : (el.className ? ('.' + String(el.className).split(/\\s+/)[0]) : null), text: text.substring(0, 400) };
    } catch {
      return null;
    }
  }

  function extractScoreFromDom() {
    try {
      const selectors = [
        '[id*="score" i]', '[class*="score" i]',
        '[id*="result" i]', '[class*="result" i]',
        '[data-score]', '[data-points]', '[data-max]'
      ];
      const candidates = Array.from(document.querySelectorAll(selectors.join(',')));
      for (const el of candidates) {
        const dataScore = el.getAttribute('data-score') || el.getAttribute('data-points');
        const dataMax = el.getAttribute('data-max');
        if (dataScore != null) {
          const score = Number(dataScore);
          const max = dataMax != null ? Number(dataMax) : null;
          if (!Number.isNaN(score)) return { score, max: Number.isNaN(max) ? null : max };
        }
        const text = (el.textContent || '').trim();
        const scoreRegex = new RegExp('(\\\\d+)\\\\s*\\\\/\\\\s*(\\\\d+)');
        const m = text.match(scoreRegex);
        if (m) return { score: Number(m[1]), max: Number(m[2]) };
      }
    } catch (e) {}
    return { score: null, max: null };
  }

  function computeMetrics(actions) {
    const elapsedSec = Math.max(0, Math.round((Date.now() - xapiState.startTime) / 1000));
    const actionTypes = {};
    const uniqueTargets = new Set();
    let seqSingle = 0;
    let seqMulti = 0;
    let headOn = 0;
    let extremes = 0;

    let prev = null;
    actions.forEach(a => {
      actionTypes[a.type] = (actionTypes[a.type] || 0) + 1;
      const t = a.data || {};
      const key = [t.tag, t.id, t.name, t.text, t.value].filter(Boolean).join('|');
      if (key) uniqueTargets.add(key);

      if (prev && prev.type !== a.type) {
        seqSingle++;
        const prevKey = prev.data ? [prev.data.tag, prev.data.id, prev.data.name, prev.data.text, prev.data.value].filter(Boolean).join('|') : '';
        if (prevKey && key && prevKey !== key) seqMulti++;
      }

      const textBlob = [t.text, t.id, t.name, t.value].filter(Boolean).join(' ').toLowerCase();
      if (textBlob.includes('head-on') || textBlob.includes('headon')) headOn++;
      if (textBlob.includes('mass') || textBlob.includes('speed') || textBlob.includes('velocity')) extremes++;
      prev = a;
    });

    return { elapsedSec, actionTypes, uniqueTargets: uniqueTargets.size, seqSingle, seqMulti, headOn, extremes };
  }

  function buildFeedback(summary, score, max) {
    const lines = [];
    lines.push('Feedback');
    lines.push('Interactive Response Assistant');
    lines.push(new Date().toLocaleString());
    lines.push('Interactions: ' + summary.interactions);
    lines.push('Elapsed Time: ' + summary.elapsedSec + 's');
    if (score != null) {
      lines.push('Score: ' + score + (max != null ? (' / ' + max) : ''));
    }
    lines.push('Unique Explorations: ' + summary.uniqueTargets);
    const diversity = Object.keys(summary.actionTypes)
      .map(k => k + ': ' + summary.actionTypes[k])
      .join(', ');
    lines.push('Diversity — ' + (diversity || 'n/a'));
    lines.push('Sequence Changes — single-param: ' + summary.seqSingle + ', multi-param: ' + summary.seqMulti);
    lines.push('Scenarios — head-on: ' + summary.headOn + ', extremes (mass/speed): ' + summary.extremes);
    lines.push('');
    lines.push('Action Log:');
    return lines.join('<br>');
  }

  function buildPayload(reason) {
    const actions = xapiState.actions.slice(-80);
    const scoreInfo = extractScoreFromDom();
    const metrics = computeMetrics(actions);
    const summary = {
      interactions: actions.length,
      elapsedSec: metrics.elapsedSec,
      actionTypes: metrics.actionTypes,
      uniqueTargets: metrics.uniqueTargets,
      seqSingle: metrics.seqSingle,
      seqMulti: metrics.seqMulti,
      headOn: metrics.headOn,
      extremes: metrics.extremes
    };

    const feedback = buildFeedback(summary, scoreInfo.score, scoreInfo.max);
    const actionLog = actions.slice(-12).map(a => {
      const seconds = Math.max(0, Math.round((a.t - xapiState.startTime) / 1000));
      const label = a.data && (a.data.text || a.data.id || a.data.name || a.data.tag) ?
        [a.data.text || '', a.data.id || '', a.data.name || '', a.data.tag || ''].filter(Boolean).join(' ').trim() : '';
      return { t: seconds, type: a.type, label };
    });

    return {
      score: scoreInfo.score != null ? scoreInfo.score : 0,
      max: scoreInfo.max != null ? scoreInfo.max : null,
      feedback,
      reason,
      startedAt: xapiState.startTime,
      summary,
      actionLog,
      actions,
      dashboard: captureDashboardSnapshot(),
      details: {
        summary,
        actionLog,
        dashboard: captureDashboardSnapshot()
      }
    };
  }

  function signatureOfPayload(p) {
    try {
      const last = p && p.actions && p.actions.length ? p.actions[p.actions.length - 1] : null;
      return String(p.reason) + '|' + (p.actions ? p.actions.length : 0) + '|' + (last ? last.t : 0);
    } catch {
      return String(Date.now());
    }
  }

  function flush(reason) {
    const p = buildPayload(reason);
    const sig = signatureOfPayload(p);
    if (sig === flushState.lastSig) return;
    flushState.lastSig = sig;
    flushState.lastSentAt = Date.now();
    safeStore(p);
  }

  function scheduleFlush(reason, delayMs) {
    if (flushState.timer) clearTimeout(flushState.timer);
    const d = typeof delayMs === 'number' ? delayMs : 1200;
    flushState.timer = setTimeout(() => {
      flushState.timer = null;
      // small throttle to avoid flooding
      if (Date.now() - flushState.lastSentAt < 700) return scheduleFlush(reason, 700);
      flush(reason);
    }, d);
  }

  // Keyboard tracking for text-based inputs and textareas
  // This logs each keystroke as a lightweight "key" action so it
  // appears in the same timeline analytics as clicks.
  document.addEventListener('keydown', (e) => {
    const t = e.target;
    if (!t || !t.tagName) return;

    const tag = String(t.tagName).toLowerCase();
    const type = (t.type || '').toLowerCase();

    // Limit to typical text input fields to avoid flooding from
    // every key on the whole page.
    const isTextField = (
      tag === 'input' && (!type || type === 'text' || type === 'search' || type === 'email' || type === 'number')
    ) || tag === 'textarea';

    if (!isTextField) return;

    logAction('key', {
      tag,
      id: t.id || null,
      name: t.name || null,
      value: t.value != null ? String(t.value).substring(0, 120) : null,
      text: null,
      key: String(e.key).substring(0, 40),
      code: String(e.code || '').substring(0, 40)
    });

    // Group keystrokes slightly to avoid sending on every single key.
    scheduleFlush('auto-key', 800);
  }, true);

  document.addEventListener('click', (e) => {
    // Track ALL clicks (not only buttons/links)
    const t = e.target;
    if (!t) return;
    logAction('click', {
      tag: t.tagName ? String(t.tagName).toLowerCase() : null,
      id: t.id || null,
      name: t.name || null,
      text: (t.textContent || '').trim().substring(0, 80)
    });
    scheduleFlush('auto-click');
  }, true);

  // Also flush when the tab is hidden / page unloads
  document.addEventListener('visibilitychange', () => {
    if (document.hidden) flush('pause');
  });
  window.addEventListener('beforeunload', () => {
    try { flush('unload'); } catch (e) {}
  });
  function addSaveBtn() {
    if (document.getElementById('xapi-save-button')) return;
    const b = document.createElement('button');
    b.id = 'xapi-save-button';
    b.textContent = '💾 Save to SLS';
    b.style.cssText = 'position:fixed;bottom:20px;right:20px;padding:10px 16px;background:#1976d2;color:white;border:none;border-radius:8px;z-index:9999';
    b.onclick = () => flush('manual-save');
    document.body.appendChild(b);
  }
  if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', addSaveBtn); } else { addSaveBtn(); }
})();
    </script>`;
  
  if (html.includes('</head>')) {
    return html.replace('</head>', script + '\n  </head>');
  }
  return html + script;
}

function injectQuizTracking(html) {
  const script = `
    <!-- xAPI Quiz Tracking -->
    <script>
(function() {
  const qs = { questions: [], answers: {}, attempted: 0 };
  const quizMeta = { total: 0, correct: 0, max: 0, lastScoreSent: null };
  const history = [];
  const flushState = { timer: null, lastSentAt: 0, lastSig: '' };

  // IMPORTANT:
  // - Do not send statements directly with fetch/XHR (CORS failures in SLS hosting)
  // - Use window.storeState(...) only; vendor/xAPI.js will send state + score statement.
  function safeStore(payload) {
    try {
      if (typeof window.storeState === 'function') {
        window.storeState(payload);
      }
    } catch (e) {
      // no-op
    }
  }

  function pushHistory(evt) {
    history.push(Object.assign({ t: Date.now() }, evt));
    if (history.length > 80) history.shift();
  }

  function signatureOfPayload(p) {
    try {
      const last = p && p.history && p.history.length ? p.history[p.history.length - 1] : null;
      return String(p.reason) + '|' + p.score + '/' + p.max + '|' + (p.history ? p.history.length : 0) + '|' + (last ? last.t : 0);
    } catch {
      return String(Date.now());
    }
  }

  function flush(reason) {
    const max = quizMeta.max || quizMeta.total || qs.questions.length || 0;
    const raw = (quizMeta.correct != null && quizMeta.correct > 0) ? quizMeta.correct : qs.attempted;
    const payload = {
      score: raw,
      max,
      feedback: 'Quiz ' + reason + ': ' + raw + '/' + max + ' (correct ' + quizMeta.correct + '/' + max + ')',
      reason,
      quiz: { attempted: qs.attempted, correct: quizMeta.correct, total: max },
      history: history.slice(-80)
    };
    const sig = signatureOfPayload(payload);
    if (sig === flushState.lastSig) return;
    flushState.lastSig = sig;
    flushState.lastSentAt = Date.now();
    safeStore(payload);
  }

  function scheduleFlush(reason, delayMs) {
    if (flushState.timer) clearTimeout(flushState.timer);
    const d = typeof delayMs === 'number' ? delayMs : 700;
    flushState.timer = setTimeout(() => {
      flushState.timer = null;
      if (Date.now() - flushState.lastSentAt < 500) return scheduleFlush(reason, 500);
      flush(reason);
    }, d);
  }

  // Best-effort: attempt to infer correctness from common patterns.
  // If we can't infer, we still send a score statement with raw=attempted.
  function isCorrectFromInput(inputEl) {
    try {
      if (!inputEl) return null;
      // Common patterns:
      // - data-correct="true"
      // - aria-correct / data-answer
      // - value matches data-correct-value on group container
      if (inputEl.dataset && (inputEl.dataset.correct === 'true' || inputEl.dataset.isCorrect === 'true')) return true;
      const group = inputEl.closest('[data-correct],[data-answer],[data-correct-value]');
      if (group) {
        const correct = group.dataset.correct || group.dataset.answer || group.dataset.correctValue;
        if (correct != null) return String(inputEl.value).trim() === String(correct).trim();
      }
      // Heuristic: label contains "(correct)"
      const lbl = inputEl.closest('label');
      if (lbl && /\bcorrect\b/i.test(lbl.textContent)) return true;
      return null;
    } catch {
      return null;
    }
  }

  function sendScoreToSLS(scoreRaw, max, feedback) {
    try {
      if (typeof window.storeState === 'function') {
        window.storeState({
          score: scoreRaw,
          max: max,
          feedback: feedback || '',
          quiz: { attempted: qs.attempted, correct: quizMeta.correct, total: max },
          history: history.slice(-80)
        });
      }
    } catch (e) {}
  }

  function maybeComputeAndSendScore() {
    // If we know quizMeta.max, send a score on each change.
    // Debounced by lastScoreSent.
    const max = quizMeta.max || quizMeta.total || qs.questions.length || 0;
    const raw = (quizMeta.correct != null && quizMeta.correct > 0) ? quizMeta.correct : qs.attempted;
    const scoreKey = raw + '/' + max;
    if (quizMeta.lastScoreSent === scoreKey) return;
    quizMeta.lastScoreSent = scoreKey;

    const feedback = 'Score: ' + raw + '/' + max;
    sendScoreToSLS(raw, max, feedback);
    // Score statement sending is handled by vendor/xAPI.js when storeState is called.
    scheduleFlush('auto-score');
  }

  function initQuiz() {
    const rgs = {};
    document.querySelectorAll('input[type="radio"]').forEach(inp => {
      if (!rgs[inp.name]) { rgs[inp.name] = true; qs.questions.push(inp.name); }
    });
    quizMeta.total = qs.questions.length;
    quizMeta.max = quizMeta.total;
    document.addEventListener('change', (e) => {
      if (e.target.matches('input[type="radio"], input[type="checkbox"]')) {
        const ix = qs.questions.indexOf(e.target.name);
        if (ix >= 0) {
          const ok = isCorrectFromInput(e.target);
          pushHistory({
            type: 'answer',
            q: e.target.name,
            value: String(e.target.value).substring(0, 120),
            correct: ok === true ? true : (ok === false ? false : null)
          });
          qs.attempted++;
          if (ok === true) quizMeta.correct++;
          // Flush quickly so click history / correct-wrong shows up
          scheduleFlush('answer');
          maybeComputeAndSendScore();
        }
      }
    });

    // Do not flush on visibility change for quiz mode.
    // Switching back to the parent SLS tab should not mark the activity as paused.
    window.addEventListener('beforeunload', () => { try { flush('unload'); } catch (e) {} });
  }
  function hookAnalytics() {
    try {
      if (typeof window.logAnalytics !== 'function') return;
      if (window.__xapiAnalyticsHooked) return;
      window.__xapiAnalyticsHooked = true;

      const original = window.logAnalytics;
      window.logAnalytics = function(action, challenge, studentAnswer, correctAnswer, result) {
        try {
          original.apply(this, arguments);
        } catch (e) {}

        const actionType = (action || '').toString().toLowerCase();
        const isAnswer = actionType === 'answer_check' || actionType === 'answer' || actionType === 'check_answer';
        const isStart = actionType === 'game_start' || actionType === 'start' || actionType === 'level_start';

        pushHistory({
          type: actionType || 'event',
          q: challenge ? String(challenge).substring(0, 140) : null,
          value: studentAnswer != null ? String(studentAnswer).substring(0, 140) : null,
          correct: result === 'correct' ? true : (result === 'incorrect' ? false : null),
          expected: correctAnswer != null ? String(correctAnswer).substring(0, 140) : null,
          result: result || null
        });

        if (isAnswer) {
          qs.attempted++;
          if (result === 'correct') quizMeta.correct++;
          scheduleFlush('answer_check');
          maybeComputeAndSendScore();
        } else if (isStart) {
          scheduleFlush('game_start');
        } else {
          scheduleFlush(actionType || 'analytics');
        }
      };
    } catch (e) {}
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => {
      initQuiz();
      hookAnalytics();
    });
  } else {
    initQuiz();
    hookAnalytics();
  }
})();
    </script>`;
  
  if (html.includes('</head>')) {
    return html.replace('</head>', script + '\n  </head>');
  }
  return html + script;
}

function injectAIAgentScript(html, aiScript) {
  if (html.includes('</head>')) {
    return html.replace('</head>', '\n  ' + aiScript + '\n  </head>');
  }
  return html + '\n' + aiScript;
}

export function serializeDocument(html) {
  return html;
}
