"""Detect parent company relationships for account hierarchy."""

from typing import Dict, Any, Optional, List
import re
from functools import lru_cache

__transform_id__ = "detect_parent_company"
__version__ = "1.0.0"
__updated__ = "2024-09-20"

# Compile regex patterns at module level for performance
PUNCT_PATTERN = re.compile(r"[^\w\s-]")

# Domain family mappings (child domain -> parent company)
DOMAIN_FAMILIES = {
    "whatsapp.com": "meta.com",
    "instagram.com": "meta.com",
    "oculus.com": "meta.com",
    "linkedin.com": "microsoft.com",
    "github.com": "microsoft.com",
    "youtube.com": "google.com",
    "waze.com": "google.com",
    "nest.com": "google.com",
    "twitch.tv": "amazon.com",
    "whole-foods.com": "amazon.com",
    "zappos.com": "amazon.com",
    "slack.com": "salesforce.com",
    "tableau.com": "salesforce.com",
    "mulesoft.com": "salesforce.com",
}

# Company alias mappings (child name -> parent company)
COMPANY_ALIASES = {
    "whatsapp": "Meta",
    "instagram": "Meta",
    "facebook": "Meta",
    "linkedin": "Microsoft",
    "github": "Microsoft",
    "youtube": "Google",
    "alphabet": "Google",
    "aws": "Amazon",
    "amazon web services": "Amazon",
    "slack": "Salesforce",
    "tableau": "Salesforce",
    "vmware": "Broadcom",
    "red hat": "IBM",
    "looker": "Google",
}

# Ultimate parent mappings
ULTIMATE_PARENTS = {
    "meta": "Meta Platforms Inc.",
    "google": "Alphabet Inc.",
    "microsoft": "Microsoft Corporation",
    "amazon": "Amazon.com Inc.",
    "salesforce": "Salesforce Inc.",
    "ibm": "International Business Machines Corporation",
}


@lru_cache(maxsize=1000)
def normalize_domain(url_or_domain: str) -> str:
    """Extract and normalize domain from URL or domain string."""
    if not url_or_domain:
        return ""

    s = url_or_domain.strip().lower()
    if "://" not in s:
        s = "http://" + s

    try:
        from urllib.parse import urlparse

        parsed = urlparse(s)
        domain = parsed.hostname or parsed.path.split("/")[0]
        # Remove www prefix
        if domain and domain.startswith("www."):
            domain = domain[4:]
        return domain or ""
    except:
        return ""


@lru_cache(maxsize=1000)
def canonicalize_company_name(name: str) -> str:
    """Strip suffixes and normalize company name."""
    if not name:
        return ""

    # Legal suffixes to remove
    suffixes = [
        "inc",
        "inc.",
        "llc",
        "ltd",
        "limited",
        "corp",
        "corporation",
        "gmbh",
        "sa",
        "sas",
        "kk",
        "ag",
        "plc",
        "pty",
        "co",
        "company",
    ]

    s = name.strip().lower()
    # Remove punctuation
    s = PUNCT_PATTERN.sub(" ", s)

    # Remove suffixes from the end
    tokens = s.split()
    while tokens and tokens[-1] in suffixes:
        tokens.pop()

    return " ".join(tokens)


def _lookup_wikidata_parent(
    company: Optional[str], domain: Optional[str]
) -> Optional[Dict[str, Any]]:
    """Query Wikidata relationships table for parent company."""
    try:
        import os

        if os.getenv("FM_COMPANY_TO_DOMAIN_WIKIDATA", "true").lower() != "true":
            return None

        from . import wikidata_profiles

        profile = None
        normalized_domain = (domain or "").lower()
        if normalized_domain:
            profile = wikidata_profiles.get_profile_for_domain(normalized_domain)

        if not profile and company:
            # Fallback: try to get profile by searching company name
            # This requires querying the profiles table by label
            pass  # TODO: Add label search if needed

        if not profile:
            return None

        try:
            import duckdb  # type: ignore
        except ImportError:
            return None

        db_path = os.getenv("WIKIDATA_INDEX_PATH", "config/foundrygraph.duckdb")
        if not os.path.exists(db_path):
            return None

        conn = duckdb.connect(db_path, read_only=True)
        try:
            parent_row = conn.execute(
                """
                SELECT p.wikidata_id, p.label, p.domain
                FROM company_relationships r
                JOIN company_profiles p ON r.parent_id = p.wikidata_id
                WHERE r.child_id = ? AND r.relation_type = 'parent'
                LIMIT 1
                """,
                [profile["wikidata_id"]],
            ).fetchone()
        finally:
            conn.close()

        if parent_row:
            parent_wdid, parent_label, parent_domain = parent_row
            return {
                "parent_name": parent_label,
                "parent_domain": (parent_domain or "").lower() or None,
                "wikidata_id": parent_wdid,
            }

        return None

    except Exception:
        return None


def detect_parent_company(
    company: Optional[str] = None,
    website: Optional[str] = None,
    domain: Optional[str] = None,
) -> Dict[str, Any]:
    """
    Detect parent company relationships for account hierarchy.

    Args:
        company: Company name
        website: Company website URL
        domain: Domain (if already extracted)

    Returns:
        Dict with parent company information and confidence score.

    Examples:
        >>> detect_parent_company(company="WhatsApp")
        {"child": "WhatsApp", "parent": "Meta", "ultimate_parent": "Meta Platforms Inc.",
         "relationship": "subsidiary", "confidence": 0.8}
    """

    name = (company or "").strip()
    dom = normalize_domain(website or domain or "")
    evidence = {}

    # 0. Check domain family registry first (18K+ companies)
    # Registry can tell us if domains belong to the same company (e.g., adobe.com, adobe.io)
    # This augments the hardcoded mappings below
    if dom or name:
        try:
            import os

            registry_enabled = (
                os.getenv("FM_COMPANY_TO_DOMAIN_REGISTRY", "true").lower() == "true"
            )
            if registry_enabled:
                from ..services.domain_family import get_registry

                registry = get_registry()

                # Try domain lookup first
                family_id = None
                if dom:
                    family_id = registry.get_domain_family(dom)

                # If no domain or domain not found, try company name
                if not family_id and name:
                    family_id, conf = registry.lookup_family_by_name(name)

                # If we found a family, check if it has multiple domains (indicates subsidiaries)
                if family_id:
                    domains = registry.family_to_domains.get(family_id, set())
                    if len(domains) > 1:
                        # Multiple domains = this is likely a parent with subsidiaries
                        canonical_name = registry.get_display_name(family_id)
                        if canonical_name:
                            # Get primary domain (shortest, or first alphabetically)
                            primary_domain = sorted(domains, key=lambda d: (len(d), d))[
                                0
                            ]
                            parent_name = canonical_name

                            # If the input domain/name is NOT the canonical one, it's a subsidiary
                            if dom and dom != primary_domain:
                                evidence["registry_family"] = family_id
                                return {
                                    "value": parent_name,
                                    "child": name or dom,
                                    "parent": parent_name,
                                    "ultimate_parent": parent_name,  # Registry doesn't track ultimate parents
                                    "relationship": "subsidiary",
                                    "confidence": 0.95,
                                    "evidence": evidence,
                                }
        except Exception:
            # Registry lookup failed, continue to hardcoded fallbacks
            pass

    # 0.5. Check Wikidata parent relationships (3,759 companies)
    wikidata_parent = _lookup_wikidata_parent(name, dom)
    if wikidata_parent:
        evidence["wikidata_relationship"] = wikidata_parent["wikidata_id"]
        parent_name = wikidata_parent.get("parent_name")
        parent_domain = wikidata_parent.get("parent_domain")
        return {
            "value": parent_name,
            "child": name or dom,
            "parent": parent_name,
            "parent_domain": parent_domain,
            "ultimate_parent": parent_name,  # Wikidata doesn't track ultimate parents
            "relationship": "subsidiary",
            "confidence": 0.90,
            "evidence": evidence,
        }

    # 1. Check domain family mapping (hardcoded, battle-tested edge cases)
    if dom:
        # Check exact domain match
        if dom in DOMAIN_FAMILIES:
            parent_domain = DOMAIN_FAMILIES[dom]
            parent_name = parent_domain.split(".")[0].title()
            ultimate = ULTIMATE_PARENTS.get(parent_name.lower(), parent_name)
            evidence["domain_family"] = parent_domain
            return {
                "value": parent_name,
                "child": name or dom,
                "parent": parent_name,
                "ultimate_parent": ultimate,
                "relationship": "subsidiary",
                "confidence": 1.0,
                "evidence": evidence,
            }

        # Check if it's a subdomain of a known parent
        for child_dom, parent_dom in DOMAIN_FAMILIES.items():
            if dom.endswith("." + child_dom):
                parent_name = parent_dom.split(".")[0].title()
                ultimate = ULTIMATE_PARENTS.get(parent_name.lower(), parent_name)
                evidence["domain_family"] = parent_dom
                return {
                    "value": parent_name,
                    "child": name or dom,
                    "parent": parent_name,
                    "ultimate_parent": ultimate,
                    "relationship": "subsidiary",
                    "confidence": 0.9,
                    "evidence": evidence,
                }

    # 2. Check company alias mapping
    if name:
        canon_name = canonicalize_company_name(name)

        # Check exact alias match
        if canon_name in COMPANY_ALIASES:
            parent_name = COMPANY_ALIASES[canon_name]
            ultimate = ULTIMATE_PARENTS.get(parent_name.lower(), parent_name)
            evidence["alias"] = parent_name
            return {
                "value": parent_name,
                "child": name,
                "parent": parent_name,
                "ultimate_parent": ultimate,
                "relationship": "brand",
                "confidence": 0.8,
                "evidence": evidence,
            }

        # Check if name contains known subsidiary patterns
        for alias, parent in COMPANY_ALIASES.items():
            if alias in canon_name:
                ultimate = ULTIMATE_PARENTS.get(parent.lower(), parent)
                evidence["name_pattern"] = alias
                return {
                    "value": parent,
                    "child": name,
                    "parent": parent,
                    "ultimate_parent": ultimate,
                    "relationship": "division",
                    "confidence": 0.6,
                    "evidence": evidence,
                }

    # 3. No parent detected
    return {
        "value": None,
        "child": name or dom,
        "parent": None,
        "ultimate_parent": None,
        "relationship": "unknown",
        "confidence": 0.0,
        "evidence": evidence,
    }


def detect_parent_company_batch(
    inputs: List[Dict[str, Optional[str]]],
) -> List[Dict[str, Any]]:
    """
    Batch version of detect_parent_company for better performance.

    Args:
        inputs: List of dicts with keys: company, website, domain

    Returns:
        List of parent company detection results

    Performance optimizations:
        - Loads registry once for all lookups
        - Opens DuckDB connection once for all queries
        - Batches Wikidata profile lookups
        - Reuses all static dictionaries
    """
    if not inputs:
        return []

    results = []

    # Pre-load registry once
    registry = None
    registry_enabled = False
    try:
        import os

        registry_enabled = (
            os.getenv("FM_COMPANY_TO_DOMAIN_REGISTRY", "true").lower() == "true"
        )
        if registry_enabled:
            from ..services.domain_family import get_registry

            registry = get_registry()
    except Exception:
        pass

    # Pre-open DuckDB connection once
    duckdb_conn = None
    wikidata_enabled = False
    try:
        import os

        wikidata_enabled = (
            os.getenv("FM_COMPANY_TO_DOMAIN_WIKIDATA", "true").lower() == "true"
        )

        if wikidata_enabled:
            import duckdb  # type: ignore

            db_path = os.getenv(
                "WIKIDATA_INDEX_PATH", "config/foundrygraph.duckdb"
            )
            if os.path.exists(db_path):
                duckdb_conn = duckdb.connect(db_path, read_only=True)
    except Exception:
        pass

    # Pre-load wikidata profiles module
    wikidata_profiles = None
    if wikidata_enabled:
        try:
            from . import wikidata_profiles as wp

            wikidata_profiles = wp
        except Exception:
            pass

    # Process each input
    for input_data in inputs:
        company = input_data.get("company")
        website = input_data.get("website")
        domain = input_data.get("domain")

        name = (company or "").strip()
        dom = normalize_domain(website or domain or "")
        evidence = {}

        # Phase 1: Check domain family registry (with pre-loaded registry)
        if registry and (dom or name):
            family_id = None
            if dom:
                family_id = registry.get_domain_family(dom)

            if not family_id and name:
                family_id, conf = registry.lookup_family_by_name(name)

            if family_id:
                domains = registry.family_to_domains.get(family_id, set())
                if len(domains) > 1:
                    canonical_name = registry.get_display_name(family_id)
                    if canonical_name:
                        primary_domain = sorted(domains, key=lambda d: (len(d), d))[0]
                        parent_name = canonical_name

                        if dom and dom != primary_domain:
                            evidence["registry_family"] = family_id
                            results.append(
                                {
                                    "value": parent_name,
                                    "child": name or dom,
                                    "parent": parent_name,
                                    "ultimate_parent": parent_name,
                                    "relationship": "subsidiary",
                                    "confidence": 0.95,
                                    "evidence": evidence,
                                }
                            )
                            continue

        # Phase 2: Check Wikidata (with pre-opened connection)
        if duckdb_conn and wikidata_profiles and dom:
            try:
                profile = wikidata_profiles.get_profile_for_domain(dom)

                if profile:
                    parent_row = duckdb_conn.execute(
                        """
                        SELECT p.wikidata_id, p.label, p.domain
                        FROM company_relationships r
                        JOIN company_profiles p ON r.parent_id = p.wikidata_id
                        WHERE r.child_id = ? AND r.relation_type = 'parent'
                        LIMIT 1
                        """,
                        [profile["wikidata_id"]],
                    ).fetchone()

                    if parent_row:
                        parent_wdid, parent_label, parent_domain = parent_row
                        evidence["wikidata_relationship"] = parent_wdid
                        results.append(
                            {
                                "value": parent_label,
                                "child": name or dom,
                                "parent": parent_label,
                                "parent_domain": (parent_domain or "").lower() or None,
                                "ultimate_parent": parent_label,
                                "relationship": "subsidiary",
                                "confidence": 0.90,
                                "evidence": evidence,
                            }
                        )
                        continue
            except Exception:
                pass

        # Phase 3: Check hardcoded domain families
        if dom and dom in DOMAIN_FAMILIES:
            parent_domain = DOMAIN_FAMILIES[dom]
            parent_name = parent_domain.split(".")[0].title()
            ultimate = ULTIMATE_PARENTS.get(parent_name.lower(), parent_name)
            evidence["domain_family"] = parent_domain
            results.append(
                {
                    "value": parent_name,
                    "child": name or dom,
                    "parent": parent_name,
                    "ultimate_parent": ultimate,
                    "relationship": "subsidiary",
                    "confidence": 1.0,
                    "evidence": evidence,
                }
            )
            continue

        # Phase 4: Check subdomain patterns
        if dom:
            found = False
            for child_dom, parent_dom in DOMAIN_FAMILIES.items():
                if dom.endswith("." + child_dom):
                    parent_name = parent_dom.split(".")[0].title()
                    ultimate = ULTIMATE_PARENTS.get(parent_name.lower(), parent_name)
                    evidence["domain_family"] = parent_dom
                    results.append(
                        {
                            "value": parent_name,
                            "child": name or dom,
                            "parent": parent_name,
                            "ultimate_parent": ultimate,
                            "relationship": "subsidiary",
                            "confidence": 0.9,
                            "evidence": evidence,
                        }
                    )
                    found = True
                    break

            if found:
                continue

        # Phase 5: Check company aliases
        if name:
            canon_name = canonicalize_company_name(name)

            if canon_name in COMPANY_ALIASES:
                parent_name = COMPANY_ALIASES[canon_name]
                ultimate = ULTIMATE_PARENTS.get(parent_name.lower(), parent_name)
                evidence["alias"] = parent_name
                results.append(
                    {
                        "value": parent_name,
                        "child": name,
                        "parent": parent_name,
                        "ultimate_parent": ultimate,
                        "relationship": "brand",
                        "confidence": 0.8,
                        "evidence": evidence,
                    }
                )
                continue

            # Check partial matches
            found = False
            for alias, parent in COMPANY_ALIASES.items():
                if alias in canon_name:
                    ultimate = ULTIMATE_PARENTS.get(parent.lower(), parent)
                    evidence["name_pattern"] = alias
                    results.append(
                        {
                            "value": parent,
                            "child": name,
                            "parent": parent,
                            "ultimate_parent": ultimate,
                            "relationship": "division",
                            "confidence": 0.6,
                            "evidence": evidence,
                        }
                    )
                    found = True
                    break

            if found:
                continue

        # Phase 6: No parent detected
        results.append(
            {
                "value": None,
                "child": name or dom,
                "parent": None,
                "ultimate_parent": None,
                "relationship": "unknown",
                "confidence": 0.0,
                "evidence": evidence,
            }
        )

    # Close DuckDB connection
    if duckdb_conn:
        try:
            duckdb_conn.close()
        except Exception:
            pass

    return results
