"""Classify lead sources for marketing attribution."""

from typing import Dict, Any, Optional
import re

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

# Compile regex patterns at module level for performance
TRADE_SHOW_PATTERN = re.compile(
    r"\b(trade\s*show|conference|expo|summit|booth)\b", re.IGNORECASE
)
EVENT_PATTERN = re.compile(r"\b(event|webinar|seminar|workshop)\b", re.IGNORECASE)
EMAIL_CAMPAIGN_PATTERN = re.compile(
    r"\b(email|newsletter|nurture|drip|campaign)\b", re.IGNORECASE
)

# Subchannel extraction mappings
SUBCHANNEL_MAP = {
    "google ads": "google",
    "adwords": "google",
    "google adwords": "google",
    "linkedin ads": "linkedin",
    "linkedin campaign": "linkedin",
    "facebook ads": "facebook",
    "meta ads": "facebook",
    "meta ads manager": "facebook",
    "instagram ads": "instagram",
    "instagram": "instagram",
    "twitter ads": "twitter",
    "x ads": "twitter",
    "tiktok ads": "tiktok",
    "tiktok": "tiktok",
    "pinterest ads": "pinterest",
    "pinterest": "pinterest",
    "bing ads": "bing",
    "microsoft ads": "bing",
    "yahoo ads": "yahoo",
    "baidu": "baidu",
    "duckduckgo": "duckduckgo",
    "youtube": "youtube",
    "reddit": "reddit",
    "snapchat": "snapchat",
}

# Channel mapping rules
CHANNEL_RULES = {
    "paid_search": {
        "utm_medium": ["ppc", "cpc", "paidsearch", "paid-search", "paid_search"],
        "utm_source": ["google", "bing", "yahoo", "baidu", "duckduckgo"],
        "source_patterns": ["google ads", "adwords", "bing ads", "paid search"],
    },
    "paid_social": {
        "utm_medium": ["paid-social", "paid_social", "paidsocial", "cpc", "ppc"],
        "utm_source": [
            "facebook",
            "meta",
            "linkedin",
            "twitter",
            "x",
            "instagram",
            "tiktok",
            "pinterest",
        ],
        "source_patterns": [
            "facebook ads",
            "linkedin ads",
            "twitter ads",
            "meta ads",
            "meta ads manager",
            "instagram ads",
            "tiktok ads",
            "pinterest ads",
            "snapchat ads",
            "paid social",
        ],
    },
    "social_organic": {
        "utm_medium": ["social", "social-organic", "organic-social"],
        "utm_source": [
            "facebook",
            "linkedin",
            "twitter",
            "x",
            "instagram",
            "tiktok",
            "youtube",
        ],
        "source_patterns": ["linkedin", "facebook", "twitter", "social media"],
    },
    "organic_search": {
        "utm_medium": ["organic", "organic-search"],
        "utm_source": ["google", "bing", "yahoo", "duckduckgo"],
        "source_patterns": ["organic search", "seo", "organic google"],
    },
    "email": {
        "utm_medium": ["email", "newsletter"],
        "utm_source": ["email", "newsletter", "mailchimp", "sendgrid", "hubspot"],
        "source_patterns": [
            "email campaign",
            "newsletter",
            "email marketing",
            "nurture",
        ],
    },
    "events": {
        "utm_medium": ["event", "tradeshow", "conference", "webinar"],
        "source_patterns": [
            "trade show",
            "tradeshow",
            "conference",
            "expo",
            "summit",
            "event",
            "booth",
        ],
    },
    "partner": {
        "utm_medium": ["partner", "affiliate", "referral-partner"],
        "source_patterns": ["partner", "affiliate", "reseller", "channel partner"],
    },
    "content_syndication": {
        "utm_medium": ["syndication", "content-syndication"],
        "source_patterns": [
            "content syndication",
            "syndicated content",
            "3rd party content",
        ],
    },
    "direct": {
        "utm_medium": ["direct", "none"],
        "source_patterns": ["direct traffic", "typed/bookmarked"],
    },
}


def normalize_utm_params(utm: Optional[Dict[str, Any]]) -> Dict[str, str]:
    """Normalize UTM parameters to lowercase."""
    if not utm:
        return {}

    normalized = {}
    for key, value in utm.items():
        # Normalize key to lowercase and ensure it starts with utm_
        key_lower = key.lower()
        if not key_lower.startswith("utm_"):
            key_lower = "utm_" + key_lower

        # Normalize value
        normalized[key_lower] = (str(value) or "").lower().strip()

    return normalized


def classify_lead_source(
    source: Optional[str] = None,
    utm: Optional[Dict[str, Any]] = None,
    referrer: Optional[str] = None,
) -> Dict[str, Any]:
    """
    Classify lead source for marketing attribution.

    Args:
        source: Lead source string from CRM
        utm: UTM parameters dict
        referrer: HTTP referrer URL

    Returns:
        Dict with channel classification and confidence.

    Examples:
        >>> classify_lead_source(source="Google Ads", utm={"utm_medium": "ppc"})
        {"channel": "paid_search", "subchannel": "google_ads", "confidence": 0.9}
    """

    s = (source or "").lower().strip()
    u = normalize_utm_params(utm)
    ref = (referrer or "").lower().strip()

    # Track what rule matched
    matched_rule = None
    confidence = 0.0
    channel = "other"
    subchannel = None

    # 1. Check UTM parameters (highest precedence)
    utm_medium = u.get("utm_medium", "")
    utm_source = u.get("utm_source", "")
    utm_campaign = u.get("utm_campaign", "")

    if utm_medium or utm_source:
        social_sources = CHANNEL_RULES["paid_social"].get("utm_source", [])
        social_media_mediums = CHANNEL_RULES["paid_social"].get("utm_medium", [])
        if utm_source in social_sources and utm_medium in social_media_mediums:
            channel = "paid_social"
            subchannel = utm_source.replace(" ", "_")
            matched_rule = f"utm_source={utm_source}+paid"
            confidence = 0.9

    if matched_rule is None and (utm_medium or utm_source):
        for ch, rules in CHANNEL_RULES.items():
            # Check utm_medium match
            if utm_medium and utm_medium in rules.get("utm_medium", []):
                channel = ch
                matched_rule = f"utm_medium={utm_medium}"
                confidence = 0.9

                # Determine subchannel from source
                if utm_source:
                    subchannel = utm_source.replace(" ", "_")
                break

            # Check utm_source match (lower confidence)
            if utm_source and utm_source in rules.get("utm_source", []):
                # Special case: social sources need paid indicator
                if ch == "paid_social":
                    if utm_medium in [
                        "paid",
                        "cpc",
                        "ppc",
                        "paid-social",
                        "paid_social",
                    ]:
                        channel = "paid_social"
                        matched_rule = f"utm_source={utm_source}+paid"
                        confidence = 0.85
                    else:
                        channel = "social_organic"
                        matched_rule = f"utm_source={utm_source}+organic"
                        confidence = 0.8
                else:
                    channel = ch
                    matched_rule = f"utm_source={utm_source}"
                    confidence = 0.8

                subchannel = utm_source.replace(" ", "_")
                break

    # 2. Check source string patterns (medium precedence)
    if matched_rule is None and s:
        for ch, rules in CHANNEL_RULES.items():
            patterns = rules.get("source_patterns", [])
            for pattern in patterns:
                if pattern in s:
                    channel = ch
                    matched_rule = f"source_pattern={pattern}"
                    confidence = 0.7

                    # Extract subchannel from source using comprehensive mapping
                    for key, sub in SUBCHANNEL_MAP.items():
                        if key in s:
                            subchannel = sub
                            break

                    # Fallback to basic extraction
                    if not subchannel:
                        if "google" in s:
                            subchannel = "google"
                        elif "linkedin" in s:
                            subchannel = "linkedin"
                        elif "facebook" in s or "meta" in s:
                            subchannel = "facebook"
                    break

            if matched_rule:
                break

    # 3. Check referrer (low precedence)
    if matched_rule is None and ref:
        if "google.com" in ref:
            channel = "organic_search"
            subchannel = "google"
            matched_rule = "referrer=google"
            confidence = 0.5
        elif "linkedin.com" in ref:
            channel = "social_organic"
            subchannel = "linkedin"
            matched_rule = "referrer=linkedin"
            confidence = 0.5
        elif "facebook.com" in ref:
            channel = "social_organic"
            subchannel = "facebook"
            matched_rule = "referrer=facebook"
            confidence = 0.5

    # 4. Default fallback
    if matched_rule is None:
        if s:
            matched_rule = "no_match"
            confidence = 0.3
        else:
            channel = "direct"
            matched_rule = "empty_source"
            confidence = 0.4

    return {
        "value": channel,
        "channel": channel,
        "subchannel": subchannel,
        "rule": matched_rule,
        "confidence": confidence,
        "normalized": {"source": s, "utm": u, "referrer": ref},
    }
