"""
Unified company resolution with waterfall logic.

This is the main entry point for Gremlin when resolving company names.
This is the SINGLE SOURCE OF TRUTH for resolution logic - all paths should funnel here.
"""

from __future__ import annotations

import asyncio
import logging
from typing import Any, Dict, List, Optional, TYPE_CHECKING

from ..services.gremlin_intent_router import ResolutionTarget
from fmatch.core.normalization.domain import normalize_domain
from .normalize_company import normalize_company_batch_async

if TYPE_CHECKING:
    from ..services.salesforce_gateway import SalesforceGateway

__transform_id__ = "resolve_company"
__version__ = "1.3.0"

log = logging.getLogger(__name__)


def _is_b2c_domain(domain: str) -> bool:
    """Check if domain is a consumer/freemail domain that shouldn't be enriched.

    B2C domains (gmail.com, yahoo.com, etc.) are email providers, not companies
    that should be looked up for B2B enrichment.

    Uses the comprehensive B2C bloom filter (57K+ domains) for accurate checking.
    """
    if not domain:
        return False
    normalized = normalize_domain(domain)
    if not normalized:
        return False

    if normalized.startswith(("gmail.", "yahoo.")):
        return True

    # Try the comprehensive B2C bloom filter first (57K+ domains)
    try:
        from fmatch.core.b2c_bloom_filter import is_b2c_domain as bloom_is_b2c

        return bloom_is_b2c(normalized)
    except ImportError:
        pass

    # Fallback to heuristics FREEMAIL_DOMAINS
    try:
        from fmatch.core.heuristics import FREEMAIL_DOMAINS, TEMP_DOMAINS

        return normalized in FREEMAIL_DOMAINS or normalized in TEMP_DOMAINS
    except ImportError:
        pass

    # Last resort: minimal fallback
    B2C_FALLBACK = {
        "gmail.com",
        "yahoo.com",
        "hotmail.com",
        "outlook.com",
        "live.com",
        "aol.com",
        "icloud.com",
        "me.com",
        "protonmail.com",
        "proton.me",
        "mail.com",
        "gmx.com",
        "qq.com",
        "163.com",
        "mail.ru",
        "yandex.com",
    }
    return normalized in B2C_FALLBACK

_CONFIDENCE_MAP = {
    "high": 0.9,
    "hi": 0.9,
    "med": 0.7,
    "medium": 0.7,
    "low": 0.5,
    "lo": 0.5,
}


def _normalize_confidence(value: Any) -> float:
    if value is None:
        return 0.0
    if isinstance(value, (int, float)):
        score = float(value)
    elif isinstance(value, str):
        lowered = value.strip().lower()
        if lowered in _CONFIDENCE_MAP:
            score = _CONFIDENCE_MAP[lowered]
        else:
            try:
                score = float(lowered)
            except ValueError:
                return 0.0
    else:
        try:
            score = float(value)
        except (TypeError, ValueError):
            return 0.0
    if score > 1.0:
        score = score / 100.0
    return max(0.0, min(1.0, score))


def _map_resolution_source(raw: str, resolved_name: str) -> str:
    if raw == "salesforce":
        return "salesforce"
    if raw.startswith("foundrygraph"):
        return "foundrygraph"
    if raw == "registry":
        return "registry"
    if resolved_name:
        return "normalized"
    return "none"


async def resolve_company(
    company_name: str,
    domain: Optional[str] = None,
    target: ResolutionTarget = ResolutionTarget.SMART,
    sfdc_connection: Optional["SalesforceGateway"] = None,
    sfdc_threshold: int = 80,
) -> Dict[str, Any]:
    """
    Resolve company name using the specified target strategy.

    SMART waterfall:
    1. Try Salesforce match (if connected)
    2. If no match, try FoundryGraph canonical
    3. Return best result with source attribution
    """
    result: Dict[str, Any] = {
        "input_name": company_name,
        "input_domain": domain,
        "resolved_name": "",
        "resolved_source": "none",
        "resolved_confidence": 0.0,
        "source": "none",
        "confidence": 0.0,
        "tenant_account_id": None,
        "tenant_account_name": None,
        "sfdc_account_id": None,
        "sfdc_account_name": None,
        "foundrygraph_company_id": None,
        "resolution_notes": "",
        "candidates": None,
        # Error tracking
        "sfdc_unavailable": False,
        "sfdc_error_reason": None,
        # B2C skip tracking
        "skipped": False,
        "skip_reason": None,
    }

    # Early exit: Skip B2C/freemail domains - these are email providers, not B2B companies
    # Only skip if we have no company name to resolve (domain-only resolution)
    cleaned_company = (company_name or "").strip()
    normalized_domain = normalize_domain(domain) if domain else None
    if normalized_domain and _is_b2c_domain(normalized_domain) and not cleaned_company:
        log.debug("Skipping B2C domain: %s", normalized_domain)
        result["skipped"] = True
        result["skip_reason"] = "b2c_domain"
        result["resolution_notes"] = f"Skipped B2C/freemail domain: {normalized_domain}"
        result["input_domain"] = normalized_domain
        return result

    target_value = target.value if isinstance(target, ResolutionTarget) else str(target)
    target_value = (target_value or "smart").lower()

    sfdc_available = False
    sfdc_error_reason = None
    if target in (ResolutionTarget.SMART, ResolutionTarget.SALESFORCE):
        if not sfdc_connection:
            sfdc_error_reason = "Salesforce not connected"
        else:
            try:
                status = await sfdc_connection.get_status()
                sfdc_available = bool(status.get("connected", False))
                if not sfdc_available:
                    sfdc_error_reason = status.get("reason") or "Salesforce not connected"
            except Exception as exc:
                sfdc_error_reason = f"Salesforce error: {str(exc)[:100]}"
                log.warning("SFDC status check failed for '%s': %s", company_name, sfdc_error_reason)
        if sfdc_error_reason:
            result["sfdc_unavailable"] = True
            result["sfdc_error_reason"] = sfdc_error_reason

    # SALESFORCE target (no fallback)
    if target == ResolutionTarget.SALESFORCE:
        if not sfdc_available:
            result["resolution_notes"] = sfdc_error_reason or "Salesforce not connected"
            return result

    domain_input = normalized_domain or domain
    normalized_list = await normalize_company_batch_async(
        [company_name or ""],
        sfdc_gateway=sfdc_connection,
        resolution_target=target_value,
        salesforce_connected=sfdc_available if sfdc_connection else None,
        domains=[domain_input],
    )
    normalized = normalized_list[0] if normalized_list else {}

    resolved_name = (normalized.get("value") or "").strip()
    raw_source = (normalized.get("source") or "").strip().lower()
    resolved_source = _map_resolution_source(raw_source, resolved_name)
    confidence_score = _normalize_confidence(
        normalized.get("confidence_score", normalized.get("confidence"))
    )

    if resolved_name:
        result.update(
            {
                "resolved_name": resolved_name,
                "resolved_source": resolved_source,
                "resolved_confidence": confidence_score,
                "source": resolved_source,
                "confidence": confidence_score,
                "canonical_name": resolved_name,
                "foundrygraph_company_id": normalized.get("wikidata_id"),
            }
        )

        if resolved_source == "salesforce":
            result.update(
                {
                    "sfdc_account_id": normalized.get("account_id"),
                    "sfdc_account_name": normalized.get("account_name"),
                    "match_method": normalized.get("match_method"),
                    "resolution_notes": f"Matched to Salesforce Account via {normalized.get('match_method') or 'unknown'}",
                }
            )
        elif raw_source == "registry":
            result["resolution_notes"] = "Matched to local registry"
        elif raw_source == "foundrygraph_fuzzy":
            result["resolution_notes"] = "Fuzzy match via FoundryGraph"
        elif raw_source.startswith("foundrygraph"):
            result["resolution_notes"] = "Canonical name from FoundryGraph"
        elif resolved_source == "normalized":
            result["resolution_notes"] = "Normalized name (no resolvable key)"
        return result

    if target == ResolutionTarget.SALESFORCE:
        result["resolution_notes"] = sfdc_error_reason or "No Salesforce Account match found"
    elif target == ResolutionTarget.SMART:
        if sfdc_error_reason:
            result["resolution_notes"] = (
                f"SFDC unavailable ({sfdc_error_reason})—no FoundryGraph match found"
            )
        else:
            result["resolution_notes"] = "No match in Salesforce or FoundryGraph"
    else:
        result["resolution_notes"] = "No FoundryGraph match found"

    return result


def _run_sync(coro):
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        return asyncio.run(coro)
    else:  # pragma: no cover - defensive fallback
        if isinstance(coro, asyncio.Task):
            return coro
        if loop.is_running():
            # Fire-and-wait fallback for environments that already have a loop
            return loop.create_task(coro)
        return loop.run_until_complete(coro)


def transform(values: List[str], params: Dict[str, Any] | None = None) -> List[List[str]]:
    """
    Batch transform for resolve_company.

    Returns columns: [resolved_name, source, confidence, sfdc_account_id, notes]
    """
    params = params or {}
    try:
        target = ResolutionTarget(params.get("target", "smart"))
    except Exception:
        target = ResolutionTarget.SMART

    sfdc_connection = params.get("sfdc_connection")

    results: List[List[str]] = []
    for val in values:
        coro = resolve_company(
            company_name=val,
            domain=params.get("domain"),
            target=target,
            sfdc_connection=sfdc_connection,
        )
        resolved = _run_sync(coro)
        results.append(
            [
                resolved.get("resolved_name", ""),
                resolved.get("source", ""),
                str(resolved.get("confidence", 0)),
                resolved.get("sfdc_account_id", ""),
                resolved.get("resolution_notes", ""),
            ]
        )
    return results
