import { Change } from 'diff';
import * as cheerio from 'cheerio';
import OpenAI from 'openai';

/**
 * Generate a simple human-readable summary of changes
 * Uses HTML parsing to count added/removed elements
 *
 * Example output: "3 text blocks changed, 2 new links added, 1 image removed"
 */
export function generateSimpleSummary(
  diff: Change[],
  htmlOld: string,
  htmlNew: string
): string {
  const parts: string[] = [];

  // Extract text previews from diff
  const textPreviews = extractTextPreviews(diff);

  // Count changed text blocks from diff
  const changedTextBlocks = countChangedTextNodes(diff);
  if (changedTextBlocks > 0) {
    parts.push(`${changedTextBlocks} text block${changedTextBlocks > 1 ? 's' : ''} changed`);
  }

  // Parse HTML to count structural changes
  const addedLinks = countAddedElements(htmlOld, htmlNew, 'a');
  const removedLinks = countRemovedElements(htmlOld, htmlNew, 'a');
  const addedImages = countAddedElements(htmlOld, htmlNew, 'img');
  const removedImages = countRemovedElements(htmlOld, htmlNew, 'img');
  const addedTables = countAddedElements(htmlOld, htmlNew, 'table');
  const removedTables = countRemovedElements(htmlOld, htmlNew, 'table');
  const addedLists = countAddedElements(htmlOld, htmlNew, 'ul') + countAddedElements(htmlOld, htmlNew, 'ol');
  const removedLists = countRemovedElements(htmlOld, htmlNew, 'ul') + countRemovedElements(htmlOld, htmlNew, 'ol');

  // Add links
  if (addedLinks > 0) {
    parts.push(`${addedLinks} new link${addedLinks > 1 ? 's' : ''} added`);
  }
  if (removedLinks > 0) {
    parts.push(`${removedLinks} link${removedLinks > 1 ? 's' : ''} removed`);
  }

  // Add images
  if (addedImages > 0) {
    parts.push(`${addedImages} new image${addedImages > 1 ? 's' : ''}`);
  }
  if (removedImages > 0) {
    parts.push(`${removedImages} image${removedImages > 1 ? 's' : ''} removed`);
  }

  // Add tables
  if (addedTables > 0) {
    parts.push(`${addedTables} new table${addedTables > 1 ? 's' : ''}`);
  }
  if (removedTables > 0) {
    parts.push(`${removedTables} table${removedTables > 1 ? 's' : ''} removed`);
  }

  // Add lists
  if (addedLists > 0) {
    parts.push(`${addedLists} new list${addedLists > 1 ? 's' : ''}`);
  }
  if (removedLists > 0) {
    parts.push(`${removedLists} list${removedLists > 1 ? 's' : ''} removed`);
  }

  // If no specific changes found, return generic message
  if (parts.length === 0 && textPreviews.length === 0) {
    return 'Content updated';
  }

  // Build summary with text previews
  let summary = parts.join(', ');

  // Add text preview if available
  if (textPreviews.length > 0) {
    const previewText = textPreviews.slice(0, 2).join(' → ');
    if (summary) {
      summary += `. Changed: "${previewText}"`;
    } else {
      summary = `Text changed: "${previewText}"`;
    }
  }

  return summary.charAt(0).toUpperCase() + summary.slice(1);
}

/**
 * Extract short text previews from diff (focus on visible text, ignore code)
 */
function extractTextPreviews(diff: Change[]): string[] {
  const previews: string[] = [];
  const maxPreviewLength = 50;

  for (const part of diff) {
    if (part.added || part.removed) {
      // Skip if it looks like CSS, JavaScript, or technical code
      if (looksLikeCode(part.value)) {
        continue;
      }

      // Extract visible text (strip HTML tags)
      const text = extractVisibleText(part.value);

      if (text.length > 5) {  // Only include meaningful text
        const truncated = text.length > maxPreviewLength
          ? text.substring(0, maxPreviewLength) + '...'
          : text;

        const prefix = part.added ? '+' : '-';
        previews.push(`${prefix}${truncated}`);
      }
    }
  }

  return previews.slice(0, 4);  // Limit to 4 previews
}

/**
 * Check if text looks like code (CSS, JS, etc.)
 */
function looksLikeCode(text: string): boolean {
  const codePatterns = [
    /^\s*[\.\#@]\w+\s*\{/m,     // CSS selectors (.class {, #id {, @media {)
    /{\s*[a-z-]+\s*:\s*[^}]+}/i, // CSS properties ({ color: red; })
    /function\s*\(/,             // JavaScript function
    /\bconst\s+\w+\s*=/,         // JS const declaration
    /\bvar\s+\w+\s*=/,           // JS var declaration
    /\blet\s+\w+\s*=/,           // JS let declaration
    /^\s*<script/im,             // Script tags
    /^\s*<style/im,              // Style tags
    /[a-z-]+\s*:\s*[#\d]+[a-z]+\s*;/i, // CSS property: value;
  ];

  return codePatterns.some(pattern => pattern.test(text));
}

/**
 * Extract visible text from HTML, focusing on meaningful content
 */
function extractVisibleText(html: string): string {
  return html
    .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')  // Remove scripts
    .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')    // Remove styles
    .replace(/<[^>]*>/g, ' ')                            // Remove HTML tags
    .replace(/&nbsp;/g, ' ')                             // Replace &nbsp;
    .replace(/&[a-z]+;/gi, ' ')                          // Remove other HTML entities
    .replace(/\{[^}]*\}/g, '')                           // Remove CSS blocks { }
    .replace(/\s+/g, ' ')                                // Normalize whitespace
    .trim();
}

/**
 * Count changed text nodes from diff
 */
function countChangedTextNodes(diff: Change[]): number {
  let count = 0;

  diff.forEach((part) => {
    if (part.added || part.removed) {
      // Count non-empty lines as text blocks
      const lines = part.value.split('\n').filter(line => line.trim().length > 0);
      count += lines.length;
    }
  });

  // Divide by 2 because additions and removals are counted separately
  // but represent the same change
  return Math.ceil(count / 2);
}

/**
 * Count elements added between old and new HTML
 */
function countAddedElements(htmlOld: string, htmlNew: string, tag: string): number {
  try {
    const $old = cheerio.load(htmlOld);
    const $new = cheerio.load(htmlNew);

    const oldCount = $old(tag).length;
    const newCount = $new(tag).length;

    return Math.max(0, newCount - oldCount);
  } catch (error) {
    console.error(`[Summarizer] Error counting added ${tag}:`, error);
    return 0;
  }
}

/**
 * Count elements removed between old and new HTML
 */
function countRemovedElements(htmlOld: string, htmlNew: string, tag: string): number {
  try {
    const $old = cheerio.load(htmlOld);
    const $new = cheerio.load(htmlNew);

    const oldCount = $old(tag).length;
    const newCount = $new(tag).length;

    return Math.max(0, oldCount - newCount);
  } catch (error) {
    console.error(`[Summarizer] Error counting removed ${tag}:`, error);
    return 0;
  }
}

/**
 * Generate AI-powered summary for large changes (≥10%)
 * Uses OpenAI API (GPT-4o-mini for cost-efficiency)
 */
export async function generateAISummary(
  diff: Change[],
  changePercentage: number
): Promise<string> {
  try {
    // Check if API key is configured
    if (!process.env.OPENAI_API_KEY) {
      console.warn('[Summarizer] OPENAI_API_KEY not configured, falling back to simple summary');
      throw new Error('OPENAI_API_KEY not configured');
    }

    const client = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
    });

    // Format diff for AI (reduce token count)
    const formattedDiff = formatDiffForAI(diff);

    const prompt = `Analyze this website change and create a concise summary for non-programmers.
Focus on IMPORTANT changes only. Medium detail level.

Change percentage: ${changePercentage.toFixed(2)}%

Diff:
${formattedDiff}

Format: "Section name: What changed. Details if important."
Example: "Pricing section updated: 3 prices increased. 2 new product links in footer."
Keep it under 100 words. Be specific about what changed, not how.`;

    const completion = await client.chat.completions.create({
      model: 'gpt-4o-mini', // Fastest, cheapest
      max_tokens: 200,
      messages: [{ role: 'user', content: prompt }],
      temperature: 0.3, // Low temperature for consistent, factual summaries
    });

    // Extract text from response
    const summary = completion.choices[0]?.message?.content || 'Content updated';

    console.log('[Summarizer] AI summary generated:', summary);
    return summary.trim();
  } catch (error) {
    console.error('[Summarizer] AI summary failed:', error);
    throw error; // Re-throw to allow fallback to simple summary
  }
}

/**
 * Format diff for AI to reduce token count
 * Extracts only added/removed content, limits to ~1000 characters
 */
function formatDiffForAI(diff: Change[]): string {
  const lines: string[] = [];
  let charCount = 0;
  const maxChars = 1000;

  for (const part of diff) {
    if (charCount >= maxChars) break;

    if (part.added) {
      const prefix = '+ ';
      const content = part.value.trim().substring(0, 200); // Limit each chunk
      lines.push(prefix + content);
      charCount += prefix.length + content.length;
    } else if (part.removed) {
      const prefix = '- ';
      const content = part.value.trim().substring(0, 200);
      lines.push(prefix + content);
      charCount += prefix.length + content.length;
    }
    // Skip unchanged parts to save tokens
  }

  return lines.join('\n');
}
