"""Domain-based enrichment: website checks, phone scraping, competitor detection, website content fetching.

Ported from gardener-fork/lib/enrichment.py (non-Brave parts).
Zero API cost - just HTTP requests to the business's own website.
"""

import re
import ssl
import socket
import urllib.request
import urllib.error
import threading
import json
import os
from html.parser import HTMLParser

from pathlib import Path
from typing import Tuple, List, Dict, Optional


CONFIG_PATH = Path("./config/gardener.json")

COMPETITOR_BRANDS = [
    "Ricoh", "Canon", "Konica Minolta", "HP", "Hewlett-Packard",
    "Brother", "Sharp", "Kyocera", "Toshiba", "Lexmark",
    "Samsung Printer", "Epson", "Dell Printer"
]


def _load_config() -> Dict:
    """Load gardener.json config for free provider domains."""
    if not CONFIG_PATH.exists():
        return {}
    try:
        with open(CONFIG_PATH, 'r') as f:
            return json.load(f)
    except (json.JSONDecodeError, IOError):
        return {}


def _get_free_provider_domains() -> set:
    """Get free provider domains from config. Single source of truth."""
    cfg = _load_config()
    bonus_cfg = cfg.get("contact_info_bonus", {})
    domains = bonus_cfg.get("free_provider_domains", [])
    return set(d.lower() for d in domains)


def extract_domain_from_email(email: str) -> Optional[str]:
    """Returns the domain or None if email is missing or uses a free provider.
    
    Args:
        email: Email address string
    
    Returns:
        Domain string or None
    """
    if not email or "@" not in email:
        return None
    
    domain = email.split("@")[-1].lower().strip()
    
    if domain in _get_free_provider_domains():
        return None
    
    return domain


def check_website_exists(domain: str, timeout: int = 3) -> Tuple[bool, Optional[str]]:
    """HEAD-check the domain. Returns (exists, url). Tries HTTPS then HTTP.
    
    Args:
        domain: Domain to check (e.g., "example.com")
        timeout: Request timeout in seconds
    
    Returns:
        Tuple of (exists: bool, url: str or None)
    """
    if not domain:
        return False, None
    
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    
    for proto in ["https", "http"]:
        url = f"{proto}://{domain}"
        try:
            req = urllib.request.Request(
                url,
                headers={"User-Agent": "ABS-Enrich/1.0"},
                method="HEAD"
            )
            urllib.request.urlopen(req, timeout=timeout, context=ctx)
            return True, url
        except urllib.error.HTTPError:
            return True, url
        except (ConnectionRefusedError, ConnectionResetError):
            break
        except Exception:
            continue
    
    www_domain = f"www.{domain}"
    for proto in ["https", "http"]:
        url = f"{proto}://{www_domain}"
        try:
            req = urllib.request.Request(
                url,
                headers={"User-Agent": "ABS-Enrich/1.0"},
                method="HEAD"
            )
            urllib.request.urlopen(req, timeout=timeout, context=ctx)
            return True, url
        except urllib.error.HTTPError:
            return True, url
        except Exception:
            continue
    
    return False, None


def scrape_phones_from_website(url: str, timeout: int = 3) -> List[str]:
    """Scrape phone numbers from /contact and / pages. Returns list (possibly empty).
    
    Args:
        url: Base website URL
        timeout: Request timeout in seconds
    
    Returns:
        List of phone number strings
    """
    if not url:
        return []
    
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    
    base = url.rstrip('/')
    paths = ["/contact", "/contact-us", "/about", "/"]
    phones = []
    
    phone_patterns = [
        r'\b\d{3}[-.)]\s*\d{3}[-.]\s*\d{4}\b',
        r'\b\(\d{3}\)\s*\d{3}[-.]\s*\d{4}\b',
        r'\b\d{3}\.\d{3}\.\d{4}\b',
    ]
    
    for path in paths:
        try:
            page_url = f"{base}{path}"
            req = urllib.request.Request(
                page_url,
                headers={"User-Agent": "ABS-Enrich/1.0"}
            )
            resp = urllib.request.urlopen(req, timeout=timeout, context=ctx)
            html = resp.read().decode("utf-8", errors="ignore")[:200000]
            
            for pat in phone_patterns:
                found = re.findall(pat, html)
                phones.extend(found)
                
        except Exception:
            continue
    
    return list(set(phones))


def competitor_check(domain: str, timeout: int = 3) -> Dict:
    """Check the lead's site for competitor brand mentions.
    
    Args:
        domain: Domain to check
        timeout: Request timeout in seconds
    
    Returns:
        Dict with:
        - displacement (bool): True if competitor found
        - brands_found (list): List of competitor brands found
        - summary (str): Human-readable summary
    """
    if not domain:
        return {"displacement": False, "brands_found": [], "summary": ""}
    
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    
    found = []
    
    for path in ["/", "/about", "/contact"]:
        if found:
            break
        
        try:
            url = f"https://{domain}{path}"
            req = urllib.request.Request(
                url,
                headers={"User-Agent": "ABS-Enrich/1.0"}
            )
            resp = urllib.request.urlopen(req, timeout=timeout, context=ctx)
            html = resp.read().decode("utf-8", errors="ignore")[:200000]
            
            for brand in COMPETITOR_BRANDS:
                if brand.lower() in html.lower() and brand not in found:
                    found.append(brand)
                    
        except Exception:
            continue
    
    if found:
        return {
            "displacement": True,
            "brands_found": found,
            "summary": f"Website references {', '.join(found)} - displacement opportunity, not greenfield",
        }
    
    return {"displacement": False, "brands_found": [], "summary": ""}


def _with_timeout(func, timeout: int = 15, default=None):
    """Run func() with a wall-clock timeout using daemon thread.
    
    Args:
        func: Callable to run
        timeout: Timeout in seconds
        default: Default return value if timeout occurs
    
    Returns:
        Result of func() or default if timeout/error
    """
    result_holder = [default]
    error_holder = [None]
    
    def target():
        try:
            result_holder[0] = func()
        except Exception as e:
            error_holder[0] = e
    
    thread = threading.Thread(target=target, daemon=True)
    thread.start()
    thread.join(timeout=timeout)
    
    if thread.is_alive():
        return default
    
    if error_holder[0]:
        return default
    
    return result_holder[0]


class _ContentStripper(HTMLParser):
    """Strip HTML tags, keeping text from meaningful elements.
    
    Drops script, style, nav, footer content entirely.
    Adds spacing between block elements.
    """
    
    BLOCK_TAGS = {"p", "div", "li", "h1", "h2", "h3", "h4", "h5", "h6", "br"}
    SKIP_TAGS = {"script", "style", "nav", "footer", "header", "noscript"}
    
    def __init__(self):
        super().__init__()
        self._parts = []
        self._skip = 0
        self._last_was_block = False
    
    def handle_starttag(self, tag, attrs):
        t = tag.lower()
        if t in self.SKIP_TAGS:
            self._skip += 1
        elif t in self.BLOCK_TAGS and self._parts:
            self._last_was_block = True
    
    def handle_endtag(self, tag):
        t = tag.lower()
        if t in self.SKIP_TAGS and self._skip > 0:
            self._skip -= 1
        elif t in self.BLOCK_TAGS:
            if self._parts and not self._last_was_block:
                self._parts.append("\n")
            self._last_was_block = True
    
    def handle_data(self, data):
        if self._skip > 0:
            return
        text = data.strip()
        if text:
            if self._last_was_block:
                self._parts.append("\n")
                self._last_was_block = False
            self._parts.append(text)
            self._parts.append(" ")
    
    def get_text(self) -> str:
        return "".join(self._parts).strip()


def _strip_html(html: str) -> str:
    """Strip HTML tags from content, preserving meaningful text structure."""
    stripper = _ContentStripper()
    try:
        stripper.feed(html)
    except Exception:
        pass
    text = stripper.get_text()
    # Collapse whitespace
    text = re.sub(r"\n{3,}", "\n\n", text)
    text = re.sub(r" {2,}", " ", text)
    return text


def fetch_website_content(
    url: str,
    max_chars_per_page: int = 12000,
    max_total_chars: int = 24000,
    timeout: int = 5,
) -> dict:
    """Fetch and extract text content from a business website.
    
    Tries these paths: /, /about, /about-us, /our-team, /services.
    Strips HTML, scripts, styles, nav, footer.
    Truncates to max_chars_per_page per page, max_total_chars across all pages.
    
    Args:
        url: Base URL (e.g., https://example.com)
        max_chars_per_page: Max characters per page (~3000 tokens)
        max_total_chars: Max total characters across all pages (~6000 tokens)
        timeout: Seconds per page fetch
    
    Returns:
        Dict with pages_fetched, content, fetch_errors
    """
    base = url.rstrip("/")
    paths = ["/", "/about", "/about-us", "/our-team", "/services"]
    
    pages_fetched = []
    fetch_errors = []
    content_parts = []
    total_chars = 0
    
    # Create an SSL context that tolerates cert issues (many small biz sites)
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    
    for path in paths:
        if total_chars >= max_total_chars:
            break
        
        page_url = f"{base}{path}"
        
        def _fetch():
            req = urllib.request.Request(page_url, headers={
                "User-Agent": "Mozilla/5.0 (compatible; ABSBot/1.0; +https://abs-xerox.com)",
                "Accept": "text/html,application/xhtml+xml",
            })
            with urllib.request.urlopen(req, context=ctx, timeout=timeout) as resp:
                if resp.status == 200:
                    content_type = resp.headers.get("Content-Type", "")
                    if "text/html" not in content_type.lower():
                        raise ValueError(f"Not HTML: {content_type}")
                    raw = resp.read()
                    try:
                        return raw.decode("utf-8")
                    except UnicodeDecodeError:
                        return raw.decode("latin-1", errors="replace")
                else:
                    raise urllib.error.HTTPError(page_url, resp.status, "", resp.headers, None)
        
        try:
            html = _with_timeout(_fetch, timeout=timeout, default=None)
            if html is None:
                fetch_errors.append(f"{page_url}: timeout or error")
                continue
        except Exception as e:
            fetch_errors.append(f"{page_url}: {e}")
            continue
        
        text = _strip_html(html)
        if not text or len(text) < 50:
            continue
        
        truncated = text[:max_chars_per_page]
        content_parts.append(truncated)
        pages_fetched.append(page_url)
        total_chars += len(truncated)
        
        # Stop if homepage has plenty of content
        if path == "/" and len(text) > 3000:
            break
    
    content = " ".join(content_parts)
    if len(content) > max_total_chars:
        content = content[:max_total_chars]
    
    return {
        "pages_fetched": pages_fetched,
        "content": content,
        "fetch_errors": fetch_errors,
    }