"""
Domain Family Service for B2B Match Intelligence
=================================================
Manages domain relationships (parent/subsidiary/alias) for expanded matching.
"""

import logging
from typing import Dict, List, Set, Optional
from dataclasses import dataclass
import re

log = logging.getLogger(__name__)


@dataclass
class DomainFamily:
    """Represents a domain family relationship"""

    canonical: str
    parents: List[str]
    subsidiaries: List[str]
    aliases: List[str]

    def all_domains(self) -> Set[str]:
        """Returns all domains in the family"""
        domains = {self.canonical}
        domains.update(self.parents)
        domains.update(self.subsidiaries)
        domains.update(self.aliases)
        return domains


class DomainFamilyService:
    """
    Service for managing domain family relationships.
    This enables matching across parent/subsidiary company domains.
    """

    def __init__(self):
        # Initialize with some common domain families
        # In production, this would load from a database or API
        self._families = self._initialize_families()
        self._domain_to_canonical = self._build_lookup_index()

    def _initialize_families(self) -> Dict[str, DomainFamily]:
        """Initialize with known domain families"""
        families = {}

        # Microsoft family
        families["microsoft.com"] = DomainFamily(
            canonical="microsoft.com",
            parents=[],
            subsidiaries=[
                "linkedin.com",
                "github.com",
                "mojang.com",
                "skype.com",
                "xbox.com",
            ],
            aliases=["msft.com", "microsoft.net", "microsoftonline.com"],
        )

        # Google/Alphabet family
        families["google.com"] = DomainFamily(
            canonical="google.com",
            parents=["alphabet.com"],
            subsidiaries=[
                "youtube.com",
                "android.com",
                "fitbit.com",
                "nest.com",
                "waymo.com",
            ],
            aliases=["gmail.com", "googlemail.com", "google.net"],
        )

        # Amazon family
        families["amazon.com"] = DomainFamily(
            canonical="amazon.com",
            parents=[],
            subsidiaries=[
                "aws.amazon.com",
                "twitch.tv",
                "wholefoods.com",
                "zappos.com",
                "imdb.com",
            ],
            aliases=["amazonaws.com", "amazon.co.uk", "amazon.ca"],
        )

        # Meta family
        families["meta.com"] = DomainFamily(
            canonical="meta.com",
            parents=[],
            subsidiaries=[
                "facebook.com",
                "instagram.com",
                "whatsapp.com",
                "oculus.com",
            ],
            aliases=["fb.com", "facebook.net", "messenger.com"],
        )

        # Salesforce family
        families["salesforce.com"] = DomainFamily(
            canonical="salesforce.com",
            parents=[],
            subsidiaries=["slack.com", "tableau.com", "mulesoft.com", "heroku.com"],
            aliases=["force.com", "salesforce.net"],
        )

        # Oracle family
        families["oracle.com"] = DomainFamily(
            canonical="oracle.com",
            parents=[],
            subsidiaries=["netsuite.com", "mysql.com", "java.com", "virtualbox.org"],
            aliases=["oraclecloud.com", "oracle.net"],
        )

        # Adobe family
        families["adobe.com"] = DomainFamily(
            canonical="adobe.com",
            parents=[],
            subsidiaries=["behance.net", "magento.com", "marketo.com", "frame.io"],
            aliases=["adobe.io", "adobecc.com"],
        )

        # Healthcare examples
        families["kaiserpermanente.org"] = DomainFamily(
            canonical="kaiserpermanente.org",
            parents=[],
            subsidiaries=["kp.org"],
            aliases=["kaiser.org", "kaiserfoundationhospitals.org"],
        )

        families["providence.org"] = DomainFamily(
            canonical="providence.org",
            parents=["providence.org"],
            subsidiaries=["swedish.org", "kadlec.org", "pacmed.org"],
            aliases=["psjhealth.org"],
        )

        families["seattlechildrens.org"] = DomainFamily(
            canonical="seattlechildrens.org",
            parents=[],
            subsidiaries=[],
            aliases=["seattlechildrens.com", "seattlechildrenshospital.org"],
        )

        # Add AAH family
        families["aah.org"] = DomainFamily(
            canonical="aah.org",
            parents=[],
            subsidiaries=[],
            aliases=["americanathome.org", "aahomehealth.org"],
        )

        return families

    def _build_lookup_index(self) -> Dict[str, str]:
        """Build reverse lookup from any domain to its canonical"""
        lookup = {}
        for canonical, family in self._families.items():
            # Map all domains to canonical
            for domain in family.all_domains():
                lookup[self._normalize_domain(domain)] = canonical
        return lookup

    def _normalize_domain(self, domain: str) -> str:
        """Normalize domain for matching"""
        if not domain:
            return ""
        # Remove protocol, www, trailing slash
        domain = domain.lower().strip()
        domain = re.sub(r"^https?://", "", domain)
        domain = re.sub(r"^www\.", "", domain)
        domain = domain.rstrip("/")
        # Extract just the domain part if it's a full URL
        domain = domain.split("/")[0]
        return domain

    def get_family(self, domain: str) -> Optional[DomainFamily]:
        """
        Get the domain family for a given domain.
        Returns None if domain is not part of a known family.
        """
        normalized = self._normalize_domain(domain)
        canonical = self._domain_to_canonical.get(normalized)

        if not canonical:
            # Check if it's a subdomain of a known domain
            for known_domain in self._domain_to_canonical.keys():
                if normalized.endswith("." + known_domain):
                    canonical = self._domain_to_canonical[known_domain]
                    break

        if canonical:
            return self._families.get(canonical)

        # Return a single-domain family for unknown domains
        return DomainFamily(
            canonical=normalized, parents=[], subsidiaries=[], aliases=[]
        )

    def get_related_domains(self, domain: str, max_domains: int = 20) -> Set[str]:
        """
        Get all related domains for expanded matching.
        Returns up to max_domains related domains.
        """
        family = self.get_family(domain)
        if not family:
            return {self._normalize_domain(domain)}

        all_domains = family.all_domains()
        # Limit the number of domains to prevent explosion
        if len(all_domains) > max_domains:
            # Prioritize: canonical, parents, then subsidiaries, then aliases
            result = {family.canonical}
            for parent in family.parents[:5]:
                result.add(parent)
            for sub in family.subsidiaries[:10]:
                result.add(sub)
            for alias in family.aliases[:5]:
                if len(result) >= max_domains:
                    break
                result.add(alias)
            return result

        return all_domains

    def add_family(
        self,
        canonical: str,
        parents: List[str] = None,
        subsidiaries: List[str] = None,
        aliases: List[str] = None,
    ):
        """Add or update a domain family"""
        family = DomainFamily(
            canonical=self._normalize_domain(canonical),
            parents=[self._normalize_domain(p) for p in (parents or [])],
            subsidiaries=[self._normalize_domain(s) for s in (subsidiaries or [])],
            aliases=[self._normalize_domain(a) for a in (aliases or [])],
        )
        self._families[family.canonical] = family
        # Rebuild lookup index
        self._domain_to_canonical = self._build_lookup_index()
        log.info(
            f"Added/updated domain family for {canonical} with {len(family.all_domains())} total domains"
        )
