// Content Detector - Client-Side AI Content Analysis
// Lightweight heuristic checks that run in the extension, sends features to cloud for deep analysis

// AI-typical phrases (statistically over-represented in LLM output)
const AI_PHRASES = [
  'it\'s worth noting', 'it is worth noting', 'it\'s important to note',
  'in conclusion', 'here are some', 'there are several',
  'additionally', 'furthermore', 'moreover', 'it\'s essential',
  'plays a crucial role', 'a testament to', 'navigating the',
  'in the realm of', 'delve into', 'stands as a', 'serves as a',
  'underscores the', 'a comprehensive', 'it\'s crucial', 'in today\'s',
  'leverage', 'harness', 'foster', 'streamline',
  'multifaceted', 'nuanced', 'holistic', 'tapestry',
  'paramount', 'aforementioned', 'pertaining to'
];

export class ContentDetector {
  constructor(config = {}) {
    this.cloudUrl = config.cloudUrl || 'https://cloud.nithic.ai';
    this.dashboardUrl = config.dashboardUrl || 'https://api.portal.nithic.ai';
    this.enabled = config.enabled !== false;
    this.deviceId = config.deviceId || null;
    this.apiKey = config.apiKey || null;

    // Scan queue to avoid duplicate analysis
    this.recentHashes = new Set();
    this.maxRecentHashes = 200;

    console.log('✓ Content Detector initialized');
  }

  /**
   * Analyze text content (client-side quick scan + optional cloud deep scan)
   */
  async analyzeText(text, context = {}) {
    if (!this.enabled || !text || text.length < 100) return null;

    // Hash to avoid re-scanning
    const hash = await this._hash(text);
    if (this.recentHashes.has(hash)) return null;
    this._trackHash(hash);

    // Client-side quick analysis
    const clientResult = this._analyzeTextLocal(text);

    // Always send to cloud for deep analysis
    const cloudResult = await this._sendToCloud('analyze-text', {
      text: text.substring(0, 10000), // Cap at 10KB
      clientFeatures: {
        verdict: clientResult.verdict,
        confidence: clientResult.confidence,
        model: 'client-heuristic-v1',
        phraseScore: clientResult.phraseScore,
        structureScore: clientResult.structureScore
      },
      sourceUrl: context.url,
      sourceDomain: context.domain,
      aiService: context.aiService,
      deviceId: this.deviceId
    });

    return {
      client: clientResult,
      cloud: cloudResult,
      contentType: 'text',
      hash
    };
  }

  /**
   * Batch text analysis — the extension's passive page scanner submits many
   * short blocks at once. We forward them in a single cloud request and the
   * cloud loops them through the warm RoBERTa worker.
   *
   * `chunks` is [{ id, text, clientFeatures }]. Returns the cloud response
   * verbatim (`{ chunks: [{ id, cloudVerdict, cloudConfidence, ... }] }`).
   */
  async analyzeTextBatch(chunks, context = {}) {
    if (!this.enabled || !Array.isArray(chunks) || chunks.length === 0) return null;
    return await this._sendToCloud('analyze-text-batch', {
      chunks: chunks.map(c => ({
        id: c.id,
        text: (c.text || '').substring(0, 4000),
        clientFeatures: c.clientFeatures || null
      })),
      sourceUrl: context.url,
      sourceDomain: context.domain,
      aiService: context.aiService,
      deviceId: this.deviceId
    });
  }

  /**
   * Analyze image (fetch pixel data + cloud forensic analysis)
   */
  async analyzeImage(imageData, context = {}) {
    if (!this.enabled) return null;

    // imageData: { src, naturalWidth, naturalHeight, alt, fileSize }
    const hash = await this._hash(imageData.src || imageData.dataUrl || 'img-' + Date.now());
    if (this.recentHashes.has(hash)) return null;
    this._trackHash(hash);

    // Client-side quick analysis
    const clientResult = this._analyzeImageLocal(imageData);

    // Try to fetch actual image data for real forensic analysis
    let imageBase64 = null;
    if (imageData.src) {
      imageBase64 = await this._fetchImageAsBase64(imageData.src);
    }

    // Send to cloud WITH image data for ELA/noise/clone analysis
    const cloudResult = await this._sendToCloud('analyze-image', {
      imageBase64, // Actual pixel data for forensic analysis (null if fetch failed)
      imageMetadata: {
        width: imageData.naturalWidth,
        height: imageData.naturalHeight,
        size: imageData.fileSize || 0,
        alt: imageData.alt,
        src: imageData.src ? imageData.src.substring(0, 200) : null,
        exif: imageData.exif || null,
        c2pa: imageData.c2pa || null
      },
      clientFeatures: {
        verdict: clientResult.verdict,
        confidence: clientResult.confidence,
        model: 'client-image-heuristic-v1',
        dimensions: `${imageData.naturalWidth}x${imageData.naturalHeight}`,
        aspectRatio: clientResult.aspectRatio,
        manipulationIndicators: clientResult.manipulationIndicators,
        hash
      },
      sourceUrl: context.url,
      sourceDomain: context.domain,
      aiService: context.aiService,
      deviceId: this.deviceId
    });

    return {
      client: clientResult,
      cloud: cloudResult,
      contentType: 'image',
      hash
    };
  }

  /**
   * Fetch an image URL and convert to base64 for server-side forensic analysis.
   * Uses the extension's fetch (not subject to page CORS restrictions).
   * Returns null if fetch fails.
   */
  async _fetchImageAsBase64(src) {
    try {
      // Skip data: URIs that are already inline
      if (src.startsWith('data:')) {
        const commaIdx = src.indexOf(',');
        if (commaIdx > -1) return src.substring(commaIdx + 1);
        return null;
      }

      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), 5000);

      const response = await fetch(src, { signal: controller.signal });
      clearTimeout(timeoutId);

      if (!response.ok) return null;

      const contentType = response.headers.get('content-type') || '';
      if (!contentType.startsWith('image/')) return null;

      // Cap at 5MB to avoid memory issues
      const blob = await response.blob();
      if (blob.size > 5 * 1024 * 1024) return null;

      return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onloadend = () => {
          const base64 = reader.result;
          const commaIdx = base64.indexOf(',');
          resolve(commaIdx > -1 ? base64.substring(commaIdx + 1) : null);
        };
        reader.onerror = () => resolve(null);
        reader.readAsDataURL(blob);
      });
    } catch (e) {
      // Fetch failed (CORS, network, timeout) — cloud will fall back to heuristics
      return null;
    }
  }

  // --- Client-side text analysis ---

  _analyzeTextLocal(text) {
    const lower = text.toLowerCase();

    // Phrase frequency
    let phraseHits = 0;
    for (const phrase of AI_PHRASES) {
      if (lower.includes(phrase)) phraseHits++;
    }
    const phraseScore = Math.min(1, phraseHits / 3); // 3+ phrases = max score

    // Sentence structure uniformity
    const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 10);
    let structureScore = 0.3;
    if (sentences.length >= 3) {
      const lengths = sentences.map(s => s.trim().split(/\s+/).length);
      const avg = lengths.reduce((a, b) => a + b, 0) / lengths.length;
      const variance = lengths.reduce((sum, l) => sum + Math.pow(l - avg, 2), 0) / lengths.length;
      const cv = Math.sqrt(variance) / Math.max(avg, 1);
      // Low variance = AI-like uniformity
      if (cv < 0.2) structureScore = 0.85;
      else if (cv < 0.35) structureScore = 0.6;
      else if (cv < 0.5) structureScore = 0.4;
      else structureScore = 0.15;
    }

    // List/bullet patterns (AI loves numbered lists)
    const listPattern = /(\d+\.\s.+\n?){3,}/;
    const hasList = listPattern.test(text);
    const listBoost = hasList ? 0.15 : 0;

    const combined = Math.min(1, phraseScore * 0.5 + structureScore * 0.35 + listBoost + 0.15 * (text.length > 2000 ? 0.1 : 0));

    let verdict;
    if (combined >= 0.65) verdict = 'likely_ai';
    else if (combined >= 0.4) verdict = 'uncertain';
    else verdict = 'likely_human';

    return { verdict, confidence: Math.round(combined * 100) / 100, phraseScore, structureScore };
  }

  // --- Client-side image analysis ---

  _analyzeImageLocal(imageData) {
    const w = imageData.naturalWidth || 0;
    const h = imageData.naturalHeight || 0;
    const aspectRatio = h > 0 ? Math.round((w / h) * 100) / 100 : 0;

    let confidence = 0.3; // Base uncertainty
    const manipulationIndicators = {};

    // Common AI image dimensions (1024x1024, 512x512, 768x768, 1024x1792, etc.)
    const aiDimensions = [
      [512, 512], [768, 768], [1024, 1024], [1024, 1792], [1792, 1024],
      [1344, 768], [768, 1344], [1536, 1024], [1024, 1536]
    ];
    const isAIDimension = aiDimensions.some(([aw, ah]) => w === aw && h === ah);
    if (isAIDimension) confidence += 0.25;

    // Perfect square aspect ratio is common in AI
    if (w === h && w >= 512) confidence += 0.1;

    // Very large, clean images without EXIF = suspicious
    if (!imageData.exif && w >= 1024 && h >= 1024) confidence += 0.1;

    // Alt text containing AI tool names
    if (imageData.alt) {
      const altLower = imageData.alt.toLowerCase();
      const aiTools = ['dall-e', 'midjourney', 'stable diffusion', 'generated', 'ai image', 'firefly', 'flux'];
      if (aiTools.some(t => altLower.includes(t))) confidence += 0.3;
    }

    // Source URL hints
    if (imageData.src) {
      const srcLower = imageData.src.toLowerCase();
      if (srcLower.includes('oaidalleapi') || srcLower.includes('replicate.delivery') ||
          srcLower.includes('cdn.midjourney') || srcLower.includes('generated')) {
        confidence += 0.35;
      }
    }

    confidence = Math.min(1, confidence);

    let verdict;
    if (confidence >= 0.65) verdict = 'likely_ai';
    else if (confidence >= 0.4) verdict = 'uncertain';
    else verdict = 'likely_human';

    return { verdict, confidence: Math.round(confidence * 100) / 100, aspectRatio, manipulationIndicators };
  }

  // --- Cloud communication ---

  async _sendToCloud(endpoint, body) {
    try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), 8000);

      const response = await fetch(`${this.cloudUrl}/v1/content-detection/${endpoint}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
        signal: controller.signal
      });

      clearTimeout(timeoutId);

      if (!response.ok) {
        console.warn(`Content detection cloud error: ${response.status}`);
        return null;
      }

      return await response.json();
    } catch (e) {
      if (e.name === 'AbortError') {
        console.warn('Content detection cloud timeout');
      } else {
        console.warn('Content detection cloud error:', e.message);
      }
      return null;
    }
  }

  async _hash(input) {
    const encoder = new TextEncoder();
    const data = encoder.encode(typeof input === 'string' ? input.substring(0, 1000) : String(input));
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    return hashArray.slice(0, 8).map(b => b.toString(16).padStart(2, '0')).join('');
  }

  _trackHash(hash) {
    this.recentHashes.add(hash);
    if (this.recentHashes.size > this.maxRecentHashes) {
      const first = this.recentHashes.values().next().value;
      this.recentHashes.delete(first);
    }
  }

  /**
   * Clear cached hashes so images/text can be re-scanned (e.g., on page refresh)
   */
  clearCache() {
    this.recentHashes.clear();
  }

  destroy() {
    this.recentHashes.clear();
  }
}
