# ankur_scraper/core/url_utils.py

from urllib.parse import urlparse, urlunparse
import re
import requests
from ankur_scraper.logging_config import get_logger

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


def normalize_url(url: str) -> str:
    """
    Normalize and validate URL input to handle various user inputs:
    - example.com -> https://example.com
    - www.example.com -> https://www.example.com
    - http://example.com -> http://example.com (preserve http if specified)
    - https://example.com/ -> https://example.com (remove trailing slash from base)
    """
    if not url or not isinstance(url, str):
        raise ValueError("URL must be a non-empty string")
    
    url = url.strip()
    
    # Remove common typos and extra whitespace
    url = re.sub(r'\s+', '', url)
    
    # If URL doesn't start with a scheme, add https://
    if not url.startswith(('http://', 'https://', 'ftp://')):
        url = 'https://' + url
    
    # Parse the URL
    parsed = urlparse(url)
    
    # Validate scheme
    if parsed.scheme not in ('http', 'https'):
        raise ValueError(f"Unsupported URL scheme: {parsed.scheme}. Only http and https are supported.")
    
    # Validate netloc (domain)
    if not parsed.netloc:
        raise ValueError(f"Invalid URL: {url}. No domain found.")
    
    # Reconstruct URL without fragment and query for base URL
    # Keep path if provided, but remove trailing slash for base
    path = parsed.path.rstrip('/') if parsed.path else ''
    
    normalized = urlunparse((
        parsed.scheme,
        parsed.netloc.lower(),  # Domains are case-insensitive
        path,
        '',  # params
        '',  # query (remove for base URL)
        ''   # fragment
    ))
    
    return normalized


def validate_url_accessible(url: str, timeout: int = 5) -> tuple[bool, str]:
    """
    Check if URL is accessible and return (success, final_url).
    Handles redirects and protocol switching.
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
    }
    
    try:
        # Try with HEAD request first (faster)
        response = requests.head(url, timeout=timeout, allow_redirects=True, headers=headers)
        
        # If HEAD fails, try GET
        if response.status_code >= 400:
            response = requests.get(url, timeout=timeout, allow_redirects=True, stream=True, headers=headers)
            # Read only first 1KB to check
            next(response.iter_content(1024), None)
            response.close()
        
        if response.status_code == 200:
            return True, response.url
        else:
            error_logger.warning(f"URL returned status {response.status_code}: {url}")
            return False, url
            
    except requests.exceptions.SSLError:
        # Try switching from https to http
        if url.startswith('https://'):
            http_url = url.replace('https://', 'http://', 1)
            info_logger.info(f"SSL error, trying http instead: {http_url}")
            try:
                response = requests.get(http_url, timeout=timeout, allow_redirects=True, stream=True)
                next(response.iter_content(1024), None)
                response.close()
                if response.status_code == 200:
                    return True, response.url
            except:
                pass
        return False, url
        
    except requests.exceptions.RequestException as e:
        error_logger.error(f"Failed to validate URL {url}: {e}")
        return False, url


def get_base_domain(url: str) -> str:
    """Extract base domain from URL (e.g., 'https://sub.example.com/path' -> 'example.com')"""
    parsed = urlparse(url)
    domain_parts = parsed.netloc.split('.')
    
    # Handle cases like 'example.com', 'www.example.com', 'sub.example.com'
    if len(domain_parts) >= 2:
        return '.'.join(domain_parts[-2:])
    return parsed.netloc


def is_same_domain(url1: str, url2: str) -> bool:
    """Check if two URLs belong to the same base domain"""
    return get_base_domain(url1) == get_base_domain(url2)


def clean_url_for_comparison(url: str) -> str:
    """
    Clean URL for duplicate detection:
    - Remove fragments
    - Sort query parameters
    - Normalize path
    """
    parsed = urlparse(url)
    
    # Sort query parameters for consistent comparison
    from urllib.parse import parse_qs, urlencode
    if parsed.query:
        params = parse_qs(parsed.query)
        sorted_query = urlencode(sorted(params.items()), doseq=True)
    else:
        sorted_query = ''
    
    # Normalize path (remove duplicate slashes, trailing slash)
    path = re.sub(r'/+', '/', parsed.path).rstrip('/')
    if not path:
        path = '/'
    
    cleaned = urlunparse((
        parsed.scheme,
        parsed.netloc.lower(),
        path,
        '',
        sorted_query,
        ''  # No fragment
    ))
    
    return cleaned


def should_skip_url(url: str) -> bool:
    """
    Determine if URL should be skipped based on common patterns for
    non-content pages (login, admin, API endpoints, etc.)
    """
    skip_patterns = [
        r'/login', r'/signin', r'/sign-in',
        r'/logout', r'/signout', r'/sign-out',
        r'/register', r'/signup', r'/sign-up',
        r'/admin', r'/wp-admin', r'/dashboard',
        r'/api/', r'/rest/',
        r'/cart', r'/checkout',
        r'/feed', r'/rss',
        r'/print', r'/pdf',
        r'/search\?', r'/\?s=',
    ]
    
    url_lower = url.lower()
    return any(re.search(pattern, url_lower) for pattern in skip_patterns)