import { diffLines, Change } from 'diff';
import * as cheerio from 'cheerio';
import { IgnoreRule, KeywordRule } from '../types';

export interface DiffResult {
  changed: boolean;
  changePercentage: number;
  additions: number;
  deletions: number;
  diff: Change[];
}

export interface KeywordMatch {
  keyword: string;
  type: 'appeared' | 'disappeared' | 'count_changed';
  previousCount?: number;
  currentCount?: number;
}

export function applyIgnoreRules(html: string, rules?: IgnoreRule[]): string {
  if (!rules || rules.length === 0) return html;

  let processedHtml = html;
  const $ = cheerio.load(html);

  for (const rule of rules) {
    switch (rule.type) {
      case 'css':
        // Remove elements matching CSS selector
        $(rule.value).remove();
        processedHtml = $.html();
        break;

      case 'regex':
        // Remove text matching regex
        const regex = new RegExp(rule.value, 'gi');
        processedHtml = processedHtml.replace(regex, '');
        break;

      case 'text':
        // Remove exact text matches
        processedHtml = processedHtml.replace(
          new RegExp(rule.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'),
          ''
        );
        break;
    }
  }

  return processedHtml;
}

export function applyCommonNoiseFilters(html: string): string {
  const $ = cheerio.load(html);

  // Common cookie banner selectors
  const cookieSelectors = [
    '[class*="cookie"]',
    '[id*="cookie"]',
    '[class*="consent"]',
    '[id*="consent"]',
    '[class*="gdpr"]',
    '[id*="gdpr"]',
  ];

  cookieSelectors.forEach((selector) => {
    $(selector).remove();
  });

  let processedHtml = $.html();

  // Enhanced timestamp patterns to catch more formats
  const timestampPatterns = [
    // ISO timestamps
    /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?/gi,
    // Date formats
    /\d{1,2}\/\d{1,2}\/\d{4}/gi, // MM/DD/YYYY
    /\d{1,2}-\d{1,2}-\d{4}/gi, // MM-DD-YYYY
    /\d{4}\/\d{1,2}\/\d{1,2}/gi, // YYYY/MM/DD
    /\d{4}-\d{1,2}-\d{1,2}/gi, // YYYY-MM-DD
    // Time formats
    /\d{1,2}:\d{2}:\d{2}/gi, // HH:MM:SS
    /\d{1,2}:\d{2}\s?(AM|PM|am|pm)/gi, // HH:MM AM/PM
    // Common date patterns with month names
    /(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\.?\s+\d{1,2},?\s+\d{4}/gi,
    /\d{1,2}\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\.?\s+\d{4}/gi,
    // Timestamps with labels
    /Last updated:?\s*[\d\-\/:,\s]+(AM|PM)?/gi,
    /Updated:?\s*[\d\-\/:,\s]+(AM|PM)?/gi,
    /Modified:?\s*[\d\-\/:,\s]+(AM|PM)?/gi,
    /Posted:?\s*[\d\-\/:,\s]+(AM|PM)?/gi,
    /Published:?\s*[\d\-\/:,\s]+(AM|PM)?/gi,
    // Unix timestamps (10 or 13 digits)
    /\b\d{10,13}\b/g,
    // Relative times
    /\d+\s+(second|minute|hour|day|week|month|year)s?\s+ago/gi,
  ];

  timestampPatterns.forEach((pattern) => {
    processedHtml = processedHtml.replace(pattern, '');
  });

  return processedHtml;
}

export function compareDiffs(
  previousText: string,
  currentText: string
): DiffResult {
  const diff = diffLines(previousText, currentText);

  let additions = 0;
  let deletions = 0;
  let totalLines = 0;

  diff.forEach((part) => {
    const lines = part.value.split('\n').filter((line) => line.trim()).length;
    totalLines += lines;

    if (part.added) {
      additions += lines;
    } else if (part.removed) {
      deletions += lines;
    }
  });

  const changedLines = additions + deletions;
  const changePercentage = totalLines > 0 ? (changedLines / totalLines) * 100 : 0;

  return {
    changed: additions > 0 || deletions > 0,
    changePercentage: Math.min(changePercentage, 100),
    additions,
    deletions,
    diff,
  };
}

export function checkKeywords(
  previousText: string,
  currentText: string,
  rules?: KeywordRule[]
): KeywordMatch[] {
  if (!rules || rules.length === 0) return [];

  const matches: KeywordMatch[] = [];

  for (const rule of rules) {
    const prevMatches = rule.caseSensitive
      ? (previousText.match(new RegExp(rule.keyword, 'g')) || []).length
      : (previousText.match(new RegExp(rule.keyword, 'gi')) || []).length;

    const currMatches = rule.caseSensitive
      ? (currentText.match(new RegExp(rule.keyword, 'g')) || []).length
      : (currentText.match(new RegExp(rule.keyword, 'gi')) || []).length;

    switch (rule.type) {
      case 'appears':
        if (prevMatches === 0 && currMatches > 0) {
          matches.push({
            keyword: rule.keyword,
            type: 'appeared',
            currentCount: currMatches,
          });
        }
        break;

      case 'disappears':
        if (prevMatches > 0 && currMatches === 0) {
          matches.push({
            keyword: rule.keyword,
            type: 'disappeared',
            previousCount: prevMatches,
          });
        }
        break;

      case 'count':
        const threshold = rule.threshold || 1;
        if (Math.abs(currMatches - prevMatches) >= threshold) {
          matches.push({
            keyword: rule.keyword,
            type: 'count_changed',
            previousCount: prevMatches,
            currentCount: currMatches,
          });
        }
        break;
    }
  }

  return matches;
}

export function calculateChangeSeverity(changePercentage: number): 'minor' | 'medium' | 'major' {
  if (changePercentage > 50) return 'major';
  if (changePercentage > 10) return 'medium';
  return 'minor';
}
