# ankur_scraper/dispatcher.py

import json
import time
from ankur_scraper.core.crawler import get_internal_links, validate_url_set
from ankur_scraper.core.page_scraper import PageScraper
from ankur_scraper.logging_config import get_logger
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeout

info_logger = get_logger("info")
error_logger = get_logger("error")
general_logger = get_logger("general")


def run_with_timeout(func, timeout, *args, **kwargs):
    """Cross-platform timeout wrapper using threads."""
    with ThreadPoolExecutor(max_workers=1) as executor:
        future = executor.submit(func, *args, **kwargs)
        try:
            return future.result(timeout=timeout)
        except FuturesTimeout:
            raise TimeoutError(f"Global timeout of {timeout}s reached")


def run_scraper(url, depth, use_dynamic=False, timeout=10, user_agent=None, max_pages=200, max_runtime=600):
    """
    Main scraper orchestrator.
    
    Args:
        url: Base URL to scrape (should be normalized with http:// or https://)
        depth: Crawl depth
        use_dynamic: Whether to use Playwright for JS-heavy sites
        timeout: Request timeout in seconds
        user_agent: Custom user agent string
        max_pages: Maximum number of pages to crawl
        max_runtime: Maximum runtime in seconds
    
    Returns:
        Dict with 'data' (list of scraped content) and 'summary' (stats)
    """
    # Validate URL has protocol
    if not url.startswith(('http://', 'https://')):
        error_logger.error(f"[bold red]URL must include protocol (http:// or https://):[/] {url}")
        return {
            "data": [],
            "summary": {
                "successful_pages": 0,
                "failed_pages": 0,
                "total_sections": 0,
                "error": "Invalid URL - missing protocol"
            }
        }
    
    info_logger.info(f"[bold cyan]🌐 Starting crawl for:[/] {url}")
    info_logger.info(f"[dim]Parameters: depth={depth}, dynamic={use_dynamic}, timeout={timeout}s, max_pages={max_pages}[/]")

    # Step 1: Discover internal links
    try:
        internal_links = get_internal_links(
            url,
            depth,
            user_agent=user_agent,
            max_pages=max_pages,
            delay=0.5  # Be polite with rate limiting
        )
    except ValueError as e:
        error_logger.error(f"[bold red]❌ Invalid URL:[/] {e}")
        return {
            "data": [],
            "summary": {
                "successful_pages": 0,
                "failed_pages": 0,
                "total_sections": 0,
                "error": str(e)
            }
        }
    except Exception as e:
        error_logger.error(f"[bold red]❌ Failed during crawling:[/] {e}")
        return {
            "data": [],
            "summary": {
                "successful_pages": 0,
                "failed_pages": 0,
                "total_sections": 0,
                "error": str(e)
            }
        }

    # Step 1.5: Validate all URLs have protocols
    valid_links, link_errors = validate_url_set(internal_links)
    
    if link_errors:
        error_logger.warning(f"[yellow]Found {len(link_errors)} invalid URLs (filtered out)[/]")
        for error in link_errors[:5]:  # Show first 5 errors
            error_logger.debug(f"  - {error}")
    
    internal_links = valid_links
    
    info_logger.info(f"[bold green]🔗 Found {len(internal_links)} valid internal links[/]")
    
    if not internal_links:
        error_logger.error("[bold red]No internal links found. Check if URL is accessible.[/]")
        return {
            "data": [],
            "summary": {
                "successful_pages": 0,
                "failed_pages": 0,
                "total_sections": 0,
                "error": "No links found"
            }
        }

    # Step 2: Scrape each page
    def crawl_job():
        results = []
        success_count = 0
        fail_count = 0
        total_sections = 0

        with PageScraper(use_dynamic=use_dynamic, timeout=timeout, user_agent=user_agent) as scraper:
            for idx, link in enumerate(internal_links, 1):
                # Final URL validation before scraping
                if not link.startswith(('http://', 'https://')):
                    error_logger.error(f"❌ [red]Invalid URL (no protocol):[/] {link}")
                    fail_count += 1
                    continue
                
                start_time = time.time()
                general_logger.info(f"[dim]Scraping [{idx}/{len(internal_links)}]:[/] {link}")

                try:
                    scraped_sections = scraper.scrape(link)
                    
                    for section_name, content in scraped_sections:
                        results.append({
                            "content": content,
                            "metadata": {
                                "section": section_name,
                                "source_url": link,
                                "extraction_time": time.strftime("%Y-%m-%d %H:%M:%S")
                            }
                        })
                    
                    duration = round(time.time() - start_time, 2)
                    info_logger.info(f"✅ [green]Success[/] - {len(scraped_sections)} sections in {duration}s")
                    success_count += 1
                    total_sections += len(scraped_sections)
                    
                except Exception as e:
                    error_logger.error(f"❌ [red]Failed[/] - {str(e)[:80]}")
                    fail_count += 1

        return results, success_count, fail_count, total_sections

    # Step 3: Run with timeout
    try:
        results, success_count, fail_count, total_sections = run_with_timeout(crawl_job, max_runtime)
    except TimeoutError:
        error_logger.error(f"⏰ Global timeout of {max_runtime}s reached, stopping crawl.")
        results, success_count, fail_count, total_sections = [], 0, 0, 0

    # Step 4: Print summary
    info_logger.info("\n[bold yellow]📊 Summary:[/]")
    info_logger.info(f"  ✅ [green]Successful pages:[/] {success_count}")
    info_logger.info(f"  ❌ [red]Failed pages:[/]     {fail_count}")
    info_logger.info(f"  📄 [cyan]Total sections:[/]   {total_sections}")

    return {
        "data": results,
        "summary": {
            "successful_pages": success_count,
            "failed_pages": fail_count,
            "total_sections": total_sections,
            "total_pages_found": len(internal_links)
        }
    }