import * as cheerio from 'cheerio';
import 'server-only';

// Dynamic cheerio import used to avoid client-side bundling issues
// Using any type for CheerioAPI to prevent bundling issues
import { 
  TextPatternStrategy, 
  TextBlockStrategy, 
  HtmlBetweenStrategy,
  LinkTextStrategy 
} from '../types';

export function extractByTextPattern(
  html: string, 
  config: TextPatternStrategy
): string {
  let searchContent = html;
  
  // Limit search scope if specified
  if (config.searchAfter) {
    const afterIndex = html.indexOf(config.searchAfter);
    if (afterIndex !== -1) {
      searchContent = html.substring(afterIndex);
    }
  }
  
  if (config.searchBefore) {
    const beforeIndex = searchContent.indexOf(config.searchBefore);
    if (beforeIndex !== -1) {
      searchContent = searchContent.substring(0, beforeIndex);
    }
  }
  
  const regex = new RegExp(config.pattern, 'i');
  const match = searchContent.match(regex);
  return match ? match[config.group || 1].trim() : '';
}

export function extractTextBlocks(
  $: cheerio.CheerioAPI,
  config: TextBlockStrategy
): string {
  const blocks: Array<{element: any, text: string}> = [];
  
  // Remove excluded elements first
  if (config.excludeSelectors) {
    config.excludeSelectors.forEach(selector => {
      $(selector).remove();
    });
  }
  
  $('*').each((_, elem) => {
    const $elem = $(elem);
    const text = $elem.clone().children().remove().end().text().trim();
    
    if (text.length >= (config.minLength || 500)) {
      blocks.push({ element: elem, text });
    }
  });
  
  // Find the block that likely contains story content
  const storyBlock = blocks.find(block => {
    if (config.containerHints && config.containerHints.length > 0) {
      const hasHints = config.containerHints.some(hint => 
        $(block.element).attr('class')?.includes(hint) ||
        $(block.element).attr('id')?.includes(hint)
      );
      return hasHints;
    }
    return blocks.length === 1;
  });
  
  if (storyBlock) {
    return $(storyBlock.element).html() || '';
  }
  
  // Fallback to largest block
  if (blocks.length === 0) {
    return '';
  }
  
  const largestBlock = blocks.reduce((prev, current) => 
    prev.text.length > current.text.length ? prev : current
  );
  
  return largestBlock ? $(largestBlock.element).html() || '' : '';
}

export function extractDeviantArtContent(
  $: cheerio.CheerioAPI,
  config: TextBlockStrategy
): string {
  // Check for mature content warning or login requirement
  const matureWarning = $('.deviation-overlay.blocked.mature, .mature-filter, .ismature').first();
  if (matureWarning.length > 0) {
    throw new Error('Content is restricted by mature content filter. Login may be required to access this story.');
  }
  
  const loginRequired = $('a[href*="join"][href*="mature"], a[href*="login"]').filter((_, elem) => {
    const text = $(elem).text().toLowerCase();
    return text.includes('log in') || text.includes('sign up');
  });
  if (loginRequired.length > 0) {
    throw new Error('Login is required to access this DeviantArt content.');
  }
  
  // Remove excluded elements first
  if (config.excludeSelectors) {
    config.excludeSelectors.forEach(selector => {
      $(selector).remove();
    });
  }
  
  // DeviantArt has multiple content structures:
  // 1. Old format: <div class="text"> containing the full story
  // 2. New format: <div class="_83r8m _2CKTq"> or similar classes containing multiple <p> elements
  // 3. Legacy journal format: .legacy-journal .text
  
  // Try the old format first (single text div)
  const textDiv = $('.text');
  if (textDiv.length > 0 && textDiv.text().trim().length >= (config.minLength || 200)) {
    return textDiv.html() || '';
  }
  
  // Try legacy journal format
  const legacyJournal = $('.legacy-journal .text, .legacy-journal .journal-text');
  if (legacyJournal.length > 0 && legacyJournal.text().trim().length >= (config.minLength || 200)) {
    return legacyJournal.html() || '';
  }
  
  // Try the new format (multiple paragraphs in specific containers)
  const newFormatSelectors = [
    'div[class*="_83r8m"] p',  // Main story content container
    'div[class*="_2CKTq"] p',  // Alternate story content container
    'div[class*="journal"] p'  // Generic journal container
  ];
  
  for (const selector of newFormatSelectors) {
    const paragraphs = $(selector);
    if (paragraphs.length > 0) {
      let totalText = '';
      paragraphs.each((_, p) => {
        totalText += $(p).text().trim();
      });
      
      // Check if this container has enough content
      if (totalText.length >= (config.minLength || 200)) {
        // Combine all paragraphs into a single HTML string
        let combinedHtml = '';
        paragraphs.each((_, p) => {
          combinedHtml += $(p).prop('outerHTML') || '';
        });
        return combinedHtml;
      }
    }
  }
  
  // Fallback to the original text-blocks strategy
  return extractTextBlocks($, config);
}

export function extractHtmlBetween(
  html: string,
  config: HtmlBetweenStrategy
): string {
  const startIndex = html.indexOf(config.startMarker);
  if (startIndex === -1) return '';
  
  const contentStart = config.includeStart ? 
    startIndex : 
    startIndex + config.startMarker.length;
    
  const endIndex = html.indexOf(config.endMarker, contentStart);
  if (endIndex === -1) {
    return html.substring(contentStart);
  }
  
  return html.substring(contentStart, endIndex).trim();
}

export function extractLinkText(
  $: cheerio.CheerioAPI,
  config: LinkTextStrategy
): string {
  let searchScope: any;
  
  if (config.searchWithin) {
    searchScope = $(config.searchWithin);
  } else {
    searchScope = $('body').length ? $('body') : $('*');
  }
  
  // Look for links near the specified text patterns
  let foundText = '';
  
  config.nearText.forEach(text => {
    if (foundText) return; // Already found
    
    searchScope.find('*').each((_: any, elem: any) => {
      const $elem = $(elem);
      const elemText = $elem.text().toLowerCase();
      
      if (elemText.includes(text.toLowerCase())) {
        // Look for nearby links
        const $link = $elem.find('a').first();
        if ($link.length) {
          foundText = $link.text().trim();
          return false; // Break out of each
        }
        
        // Check if the element itself is a link
        if ($elem.is('a')) {
          foundText = $elem.text().trim();
          return false;
        }
        
        // Look for links in the next few siblings
        const $siblings = $elem.nextAll().slice(0, 3);
        $siblings.find('a').first().each((_, link) => {
          foundText = $(link).text().trim();
          return false;
        });
      }
    });
  });
  
  return foundText;
}