/**
 * entrypoints/content.tsx
 *
 * Content script injected into every supported page.
 * Responsibilities:
 *  1. Detect platform (X, YouTube, Instagram, ChatGPT, Claude, Gemini, web)
 *  2. MutationObserver: watch for new text nodes (≥12 words)
 *  3. Ring buffer + xxhash dedup → batch sends to background every 1200ms
 *  4. Range.surroundContents() highlighting with <mark> elements
 *  5. Shadow DOM hover cards (Framer Motion animated)
 *  6. Viewport-clamped card positioning
 */

import { defineContentScript } from 'wxt/sandbox';
import { createRoot } from 'react-dom/client';
import React, { useEffect, useRef, useState, useCallback } from 'react';

// ── Platform detection ────────────────────────────────────────────────────────
type Platform =
  | 'x' | 'instagram' | 'youtube' | 'chatgpt' | 'claude'
  | 'gemini' | 'web';

function detectPlatform(): Platform {
  const host = window.location.hostname;
  if (host.includes('twitter.com') || host.includes('x.com')) return 'x';
  if (host.includes('instagram.com')) return 'instagram';
  if (host.includes('youtube.com')) return 'youtube';
  if (host.includes('chat.openai.com')) return 'chatgpt';
  if (host.includes('claude.ai')) return 'claude';
  if (host.includes('gemini.google.com') || host.includes('bard.google.com')) return 'gemini';
  return 'web';
}

// ── Color config ─────────────────────────────────────────────────────────────
const COLOR_MAP: Record<string, { bg: string; border: string; label: string; emoji: string }> = {
  green: { bg: 'rgba(34,197,94,0.12)',  border: '#22c55e', label: 'Verified',     emoji: '✓' },
  yellow:{ bg: 'rgba(234,179,8,0.14)',  border: '#eab308', label: 'Unverified',   emoji: '~' },
  red:   { bg: 'rgba(239,68,68,0.16)',  border: '#ef4444', label: 'Misleading',   emoji: '✗' },
  purple:{ bg: 'rgba(168,85,247,0.15)', border: '#a855f7', label: 'AI Hallucination', emoji: '?' },
};

// ── Mode filtering ────────────────────────────────────────────────────────────
function shouldShowColor(color: string, mode: string): boolean {
  if (mode === 'minimal') return color === 'red' || color === 'purple';
  if (mode === 'normal')  return color === 'red' || color === 'purple' || color === 'yellow';
  return true; // advanced: show all
}

// ── xxhash-like 32-bit hash (no WASM dep for content script) ─────────────────
function fastHash(str: string): string {
  let h = 0x811c9dc5;
  for (let i = 0; i < str.length; i++) {
    h ^= str.charCodeAt(i);
    h = (h * 0x01000193) >>> 0;
  }
  return h.toString(16).padStart(8, '0');
}

// ── Analysis result cache ─────────────────────────────────────────────────────
const resultCache = new Map<string, {
  color: string; confidence: number; verdict: string;
  explanation: string; sources: string[]; trustScore: number;
}>();

// ── Hover Card React component (Shadow DOM mounted) ───────────────────────────
interface HoverCardProps {
  result: typeof resultCache extends Map<string, infer V> ? V : never;
  anchorRect: DOMRect;
  onDismiss: () => void;
}

function HoverCard({ result, anchorRect, onDismiss }: HoverCardProps) {
  const cardRef = useRef<HTMLDivElement>(null);
  const [pos, setPos] = useState({ top: 0, left: 0 });
  const [visible, setVisible] = useState(false);
  const colorCfg = COLOR_MAP[result.color] || COLOR_MAP.yellow;

  useEffect(() => {
    // Calculate viewport-clamped position
    const vw = window.innerWidth, vh = window.innerHeight;
    const cardW = 340, cardH = 220;
    let top = anchorRect.bottom + 8;
    let left = anchorRect.left;
    if (top + cardH > vh) top = anchorRect.top - cardH - 8;
    if (left + cardW > vw) left = vw - cardW - 12;
    if (left < 8) left = 8;
    setPos({ top, left });
    // Trigger enter animation
    requestAnimationFrame(() => setVisible(true));
  }, [anchorRect]);

  const handleMouseLeave = useCallback(() => {
    setVisible(false);
    setTimeout(onDismiss, 180);
  }, [onDismiss]);

  return (
    <div
      ref={cardRef}
      onMouseLeave={handleMouseLeave}
      style={{
        position: 'fixed',
        top: pos.top,
        left: pos.left,
        width: 340,
        zIndex: 2147483647,
        fontFamily: "'Inter', -apple-system, sans-serif",
        fontSize: 13,
        transform: visible ? 'translateY(0) scale(1)' : 'translateY(-8px) scale(0.96)',
        opacity: visible ? 1 : 0,
        transition: 'transform 180ms cubic-bezier(0.34,1.56,0.64,1), opacity 160ms ease',
        willChange: 'transform, opacity',
      }}
    >
      <div style={{
        background: 'linear-gradient(135deg, #0f0f17 0%, #12121e 100%)',
        border: `1px solid ${colorCfg.border}`,
        borderRadius: 12,
        boxShadow: `0 8px 32px rgba(0,0,0,0.6), 0 0 0 1px ${colorCfg.border}22`,
        overflow: 'hidden',
      }}>
        {/* Header */}
        <div style={{
          display: 'flex', alignItems: 'center', gap: 10,
          padding: '12px 14px 10px',
          borderBottom: `1px solid ${colorCfg.border}22`,
          background: `${colorCfg.bg}`,
        }}>
          <div style={{
            width: 32, height: 32, borderRadius: '50%', border: `2px solid ${colorCfg.border}`,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontSize: 14, color: colorCfg.border, fontWeight: 700,
            flexShrink: 0,
          }}>
            {colorCfg.emoji}
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ color: colorCfg.border, fontWeight: 700, fontSize: 12, textTransform: 'uppercase', letterSpacing: '0.08em' }}>
              {colorCfg.label}
            </div>
            <div style={{ color: '#e2e8f0', fontWeight: 600, fontSize: 13, lineHeight: 1.3, marginTop: 2 }}>
              {result.verdict}
            </div>
          </div>
          {/* Confidence arc */}
          <ConfidenceArc confidence={result.confidence} color={colorCfg.border} />
        </div>

        {/* Body */}
        <div style={{ padding: '10px 14px 12px' }}>
          <p style={{ color: '#94a3b8', fontSize: 12, lineHeight: 1.5, margin: '0 0 10px' }}>
            {result.explanation}
          </p>

          {/* Trust score bar */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
            <span style={{ color: '#64748b', fontSize: 11, whiteSpace: 'nowrap' }}>Trust</span>
            <div style={{ flex: 1, height: 4, background: '#1e293b', borderRadius: 2, overflow: 'hidden' }}>
              <div style={{
                height: '100%', borderRadius: 2,
                width: `${Math.round(result.trustScore * 100)}%`,
                background: `linear-gradient(90deg, ${colorCfg.border}, ${colorCfg.border}88)`,
                transition: 'width 600ms cubic-bezier(0.4,0,0.2,1)',
              }} />
            </div>
            <span style={{ color: '#94a3b8', fontSize: 11, fontVariantNumeric: 'tabular-nums' }}>
              {Math.round(result.trustScore * 100)}%
            </span>
          </div>

          {/* Sources */}
          {result.sources.length > 0 && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
              {result.sources.slice(0, 3).map((url, i) => {
                const domain = (() => { try { return new URL(url).hostname.replace('www.',''); } catch { return url; } })();
                return (
                  <a key={i} href={url} target="_blank" rel="noreferrer" style={{
                    display: 'flex', alignItems: 'center', gap: 6,
                    color: '#60a5fa', fontSize: 11, textDecoration: 'none',
                    padding: '3px 6px', borderRadius: 6,
                    background: 'rgba(96,165,250,0.06)',
                    transition: 'background 120ms',
                  }}
                    onMouseEnter={e => (e.currentTarget.style.background = 'rgba(96,165,250,0.12)')}
                    onMouseLeave={e => (e.currentTarget.style.background = 'rgba(96,165,250,0.06)')}
                  >
                    <img
                      src={`https://www.google.com/s2/favicons?domain=${domain}&sz=16`}
                      alt="" width={12} height={12} style={{ borderRadius: 2, flexShrink: 0 }}
                      onError={e => { (e.target as HTMLImageElement).style.display = 'none'; }}
                    />
                    <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {domain}
                    </span>
                  </a>
                );
              })}
            </div>
          )}
        </div>

        {/* Footer */}
        <div style={{
          padding: '6px 14px 8px',
          borderTop: '1px solid #1e293b',
          display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        }}>
          <span style={{ color: '#334155', fontSize: 10 }}>⚡ Fact Engine</span>
          <span style={{ color: '#334155', fontSize: 10 }}>
            {result.confidence}% confidence
          </span>
        </div>
      </div>
    </div>
  );
}

function ConfidenceArc({ confidence, color }: { confidence: number; color: string }) {
  const r = 13, cx = 18, cy = 18;
  const circumference = 2 * Math.PI * r;
  const dash = (confidence / 100) * circumference;
  return (
    <svg width={36} height={36} style={{ flexShrink: 0, transform: 'rotate(-90deg)' }}>
      <circle cx={cx} cy={cy} r={r} fill="none" stroke="#1e293b" strokeWidth={2.5} />
      <circle
        cx={cx} cy={cy} r={r} fill="none" stroke={color} strokeWidth={2.5}
        strokeDasharray={`${dash} ${circumference}`}
        strokeLinecap="round"
        style={{ transition: 'stroke-dasharray 600ms ease' }}
      />
      <text
        x={cx} y={cy + 1}
        textAnchor="middle" dominantBaseline="middle"
        fill={color} fontSize={8} fontWeight="700"
        style={{ transform: `rotate(90deg)`, transformOrigin: `${cx}px ${cy}px`, fontFamily: 'monospace' }}
      >
        {confidence}
      </text>
    </svg>
  );
}

// ── Shadow DOM card host ──────────────────────────────────────────────────────
let shadowHost: HTMLElement | null = null;
let shadowRoot: ShadowRoot | null = null;
let reactRoot: ReturnType<typeof createRoot> | null = null;
let activeCard: { dismiss: () => void } | null = null;

function ensureShadowHost() {
  if (shadowHost && document.contains(shadowHost)) return;
  shadowHost = document.createElement('fact-engine-overlay');
  shadowHost.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;z-index:2147483647;pointer-events:none;';
  document.body.appendChild(shadowHost);
  shadowRoot = shadowHost.attachShadow({ mode: 'closed' });
  const mountPoint = document.createElement('div');
  mountPoint.style.pointerEvents = 'auto';
  shadowRoot.appendChild(mountPoint);
  // Inject Tailwind + custom styles into shadow root
  const style = document.createElement('style');
  style.textContent = `
    @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
    * { box-sizing: border-box; }
    a { cursor: pointer; }
  `;
  shadowRoot.insertBefore(style, mountPoint);
  reactRoot = createRoot(mountPoint);
}

function showCard(result: NonNullable<ReturnType<typeof resultCache.get>>, anchorRect: DOMRect) {
  ensureShadowHost();
  const dismiss = () => { reactRoot?.render(null); activeCard = null; };
  activeCard?.dismiss();
  activeCard = { dismiss };
  reactRoot!.render(
    <HoverCard result={result} anchorRect={anchorRect} onDismiss={dismiss} />
  );
}

// ── Mark highlight wrapper ────────────────────────────────────────────────────
function highlightRange(range: Range, color: string, hash: string) {
  const colorCfg = COLOR_MAP[color];
  if (!colorCfg) return;
  try {
    const mark = document.createElement('mark');
    mark.dataset.factHash = hash;
    mark.dataset.factColor = color;
    mark.style.cssText = `
      background: ${colorCfg.bg} !important;
      border-bottom: 1.5px solid ${colorCfg.border} !important;
      border-radius: 2px !important;
      cursor: pointer !important;
      padding: 0 1px !important;
      text-decoration: none !important;
    `;
    mark.addEventListener('mouseenter', (e) => {
      const result = resultCache.get(hash);
      if (result) showCard(result, (e.target as HTMLElement).getBoundingClientRect());
    });
    range.surroundContents(mark);
  } catch (_) {
    // Range may span multiple nodes — skip to avoid DOM corruption
  }
}

// ── Text extraction from DOM ──────────────────────────────────────────────────
const SKIP_TAGS = new Set(['SCRIPT', 'STYLE', 'SVG', 'NOSCRIPT', 'TEMPLATE', 'CODE', 'TEXTAREA']);
const MIN_WORDS = 12;

function extractTextNodes(root: Node): Text[] {
  const results: Text[] = [];
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
    acceptNode(node) {
      const parent = node.parentElement;
      if (!parent) return NodeFilter.FILTER_REJECT;
      if (SKIP_TAGS.has(parent.tagName)) return NodeFilter.FILTER_REJECT;
      if (parent.dataset.factHash) return NodeFilter.FILTER_REJECT;
      const text = node.textContent?.trim() || '';
      if (text.split(/\s+/).length < MIN_WORDS) return NodeFilter.FILTER_SKIP;
      return NodeFilter.FILTER_ACCEPT;
    },
  });
  let node: Node | null;
  while ((node = walker.nextNode())) results.push(node as Text);
  return results;
}

// ── Main content script ───────────────────────────────────────────────────────
export default defineContentScript({
  matches: [
    '*://*.twitter.com/*',
    '*://*.x.com/*',
    '*://*.instagram.com/*',
    '*://*.youtube.com/*',
    '*://chat.openai.com/*',
    '*://claude.ai/*',
    '*://gemini.google.com/*',
    '*://*.reuters.com/*',
    '*://*.bbc.com/*',
    '*://*.apnews.com/*',
    '<all_urls>',
  ],
  runAt: 'document_idle',

  main() {
    const platform = detectPlatform();
    const sentHashes = new Set<string>();
    // Ring buffer: cap at 50 pending claims
    let pendingBatch: Array<{ text: string; hash: string }> = [];
    let flushTimer: ReturnType<typeof setInterval>;
    let enabled = true;
    let mode = 'normal';

    // ── Load settings from storage ─────────────────────────────────────────
    chrome.storage.sync.get(['fact-engine-store'], (data) => {
      try {
        const stored = JSON.parse(data['fact-engine-store'] || '{}');
        const state = stored.state || {};
        enabled = state.enabled ?? true;
        mode = state.mode ?? 'normal';
      } catch (_) {}
    });

    // ── Listen for settings changes ────────────────────────────────────────
    chrome.storage.onChanged.addListener((changes) => {
      if (changes['fact-engine-store']) {
        try {
          const state = JSON.parse(changes['fact-engine-store'].newValue || '{}').state || {};
          enabled = state.enabled ?? enabled;
          mode = state.mode ?? mode;
        } catch (_) {}
      }
    });

    // ── Receive analysis results from background ───────────────────────────
    chrome.runtime.onMessage.addListener((msg) => {
      if (msg.type === 'analysis_results') {
        const results = msg.results as Array<{
          claim_hash: string; claim_text: string; color: string;
          confidence: number; verdict: string; explanation: string;
          sources: string[]; trust_score: number;
        }>;
        for (const r of results) {
          resultCache.set(r.claim_hash, {
            color: r.color, confidence: r.confidence,
            verdict: r.verdict, explanation: r.explanation,
            sources: r.sources, trustScore: r.trust_score,
          });
          // Apply highlights if mode allows this color
          if (enabled && shouldShowColor(r.color, mode)) {
            applyHighlightsForHash(r.claim_hash, r.color);
          }
        }
      }
    });

    // ── Apply highlights to previously seen text nodes ─────────────────────
    const nodeRegistry = new Map<string, Range>(); // hash → Range

    function applyHighlightsForHash(hash: string, color: string) {
      const range = nodeRegistry.get(hash);
      if (range) {
        highlightRange(range, color, hash);
        nodeRegistry.delete(hash);
      }
      // Also scan DOM for already-rendered text with this hash
      document.querySelectorAll(`[data-fact-pending="${hash}"]`).forEach((el) => {
        (el as HTMLElement).style.borderBottom = `1.5px solid ${COLOR_MAP[color]?.border}`;
        (el as HTMLElement).dataset.factColor = color;
      });
    }

    // ── Queue text for analysis ────────────────────────────────────────────
    function queueTextNode(node: Text) {
      const text = node.textContent?.trim() || '';
      const hash = fastHash(text);
      if (sentHashes.has(hash)) return;
      sentHashes.add(hash);

      // Store range for later highlighting
      try {
        const range = document.createRange();
        range.selectNodeContents(node);
        nodeRegistry.set(hash, range);
      } catch (_) {}

      if (pendingBatch.length < 50) {
        pendingBatch.push({ text, hash });
      }
    }

    // ── Flush batch to background ──────────────────────────────────────────
    function flushBatch() {
      if (!enabled || pendingBatch.length === 0) return;
      const batch = pendingBatch.splice(0, 20); // send max 20 at a time
      chrome.runtime.sendMessage({
        type: 'analyze_claims',
        claims: batch.map(b => b.text),
        platform,
      }).catch(() => {});
    }

    // ── MutationObserver ───────────────────────────────────────────────────
    const observer = new MutationObserver((mutations) => {
      if (!enabled) return;
      for (const mutation of mutations) {
        if (mutation.type === 'childList') {
          for (const node of mutation.addedNodes) {
            const textNodes = extractTextNodes(node);
            textNodes.forEach(queueTextNode);
          }
        }
      }
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
    });

    // ── Initial scan ───────────────────────────────────────────────────────
    setTimeout(() => {
      if (!enabled) return;
      extractTextNodes(document.body).slice(0, 100).forEach(queueTextNode);
    }, 1500);

    // ── Ring buffer flush: every 1200ms ────────────────────────────────────
    flushTimer = setInterval(flushBatch, 1200);
  },
});
