"""Match company names against Salesforce Account records via gateway."""

from __future__ import annotations

import logging
from enum import Enum
from typing import Any, Dict, List, Optional

__transform_id__ = "match_salesforce_account"
__version__ = "1.2.0"

log = logging.getLogger(__name__)


class ScoringBackend(str, Enum):
    """Scoring backend for SFDC matching."""
    LOCAL = "local"      # Deprecated: local fuzzy scoring is no longer allowed
    ENGINE = "engine"    # FoundryMatch Engine - higher quality, adds latency/credits


# Session-level caches to avoid repeat queries
_DOMAIN_CACHE: Dict[str, Optional[Dict[str, Any]]] = {}
_NAME_CACHE: Dict[str, List[Dict[str, Any]]] = {}
MAX_CACHE_SIZE = 1000

# Tuning parameters
AMBIGUITY_DELTA = 5  # Points difference to consider ambiguous (tightened from 10)
SHORT_NAME_THRESHOLD = 6  # Names shorter than this need domain or Engine


def _normalize_domain(domain: str) -> str:
    """Normalize domains consistently with core company normalization."""
    from .normalize_company import _normalize_domain as _normalize_domain_impl

    return _normalize_domain_impl(domain)


def _normalize_company_name(name: str) -> str:
    """Normalize company names for cache lookups and prefix matches."""
    from .normalize_company import _normalize_name_key as _normalize_name_key_impl

    return _normalize_name_key_impl(name)


def _normalize_threshold(value: Any, default: float = 0.8) -> float:
    try:
        threshold = float(value)
    except (TypeError, ValueError):
        threshold = default
    if threshold > 1.0:
        threshold = threshold / 100.0
    return max(0.0, min(1.0, threshold))


def _score_from_confidence(value: Any) -> int:
    if value is None:
        return 0
    if isinstance(value, (int, float)):
        score = float(value)
    elif isinstance(value, str):
        lowered = value.strip().lower()
        if lowered in {"high", "hi"}:
            score = 0.9
        elif lowered in {"med", "medium"}:
            score = 0.7
        elif lowered in {"low", "lo"}:
            score = 0.5
        else:
            try:
                score = float(lowered)
            except ValueError:
                score = 0.0
    else:
        try:
            score = float(value)
        except (TypeError, ValueError):
            score = 0.0
    if score > 1.0:
        score = score / 100.0
    return int(max(0.0, min(1.0, score)) * 100)


def _compute_match_score(
    input_name: str, candidate_name: str, normalized_input: Optional[str] = None
) -> int:
    """Compute a stable 0-100 similarity score for local fallback matching."""
    input_name = input_name or ""
    candidate_name = candidate_name or ""
    if not input_name and not candidate_name:
        return 0

    normalized_input = normalized_input or _normalize_company_name(input_name)
    normalized_candidate = _normalize_company_name(candidate_name)
    if normalized_input and normalized_input == normalized_candidate:
        return 100

    from difflib import SequenceMatcher

    score = SequenceMatcher(
        None,
        normalized_input or input_name,
        normalized_candidate or candidate_name,
    ).ratio()
    return int(score * 100)


async def match_salesforce_account(
    company_name: str,
    domain: Optional[str] = None,
    sfdc_connection: Optional[Any] = None,
    threshold: int = 80,
    scoring_backend: ScoringBackend = ScoringBackend.LOCAL,
) -> Dict[str, Any]:
    """
    Match a company against Salesforce Accounts.

    Strategy:
    1) MatchGateway-backed SFDC cache
    2) Return needs_review if gateway is unavailable

    Args:
        scoring_backend: Deprecated. MatchGateway is always used for scoring.
    """
    result: Dict[str, Any] = {
        "matched": False,
        "account_id": None,
        "account_name": None,
        "match_score": 0,
        "match_method": None,
        "source": "salesforce",
        "reason": None,
        "candidates": None,
        "scoring_backend": scoring_backend.value,
    }

    if not company_name and not domain:
        result["reason"] = "No company name or domain provided"
        return result

    if not sfdc_connection:
        result["reason"] = "Salesforce not connected"
        return result

    threshold_norm = _normalize_threshold(threshold, default=0.8)
    try:
        from .normalize_company import _match_sfdc_accounts

        matches = await _match_sfdc_accounts(
            [company_name or ""],
            sfdc_connection,
            threshold=threshold_norm,
            domains=[domain],
        )
        match = matches.get(0)
        result["scoring_backend"] = "gateway"
        if match:
            score = match.get("confidence_score")
            match_score = _score_from_confidence(score if score is not None else match.get("confidence"))
            result.update(
                {
                    "matched": True,
                    "account_id": match.get("account_id"),
                    "account_name": match.get("account_name"),
                    "match_score": match_score,
                    "match_method": match.get("match_method") or "name_fuzzy",
                }
            )
            return result
        result["reason"] = "No matching Salesforce Account found"
        return result
    except Exception as exc:
        log.warning("MatchGateway SFDC match unavailable: %s", exc)
        result.update(
            {
                "matched": False,
                "match_method": "needs_review",
                "reason": "MatchGateway unavailable",
                "scoring_backend": "gateway_unavailable",
            }
        )
        return result


def clear_cache() -> None:
    """Clear the session cache. Call between chat sessions or on demand."""
    global _DOMAIN_CACHE, _NAME_CACHE
    _DOMAIN_CACHE = {}
    _NAME_CACHE = {}


async def match_salesforce_accounts_batch(
    rows: List[Dict[str, str]],
    sfdc_connection: Any,
    threshold: int = 80,
    max_rows: int = 500,
    scoring_backend: ScoringBackend = ScoringBackend.LOCAL,
) -> List[Dict[str, Any]]:
    """
    Batch match multiple companies against Salesforce.

    Returns results in the same order as input rows.
    Caching makes repeated lookups fast even within a batch.
    """
    if len(rows) > max_rows:
        log.warning("Batch size %d exceeds limit %d, truncating", len(rows), max_rows)
        rows = rows[:max_rows]

    results = []
    for row in rows:
        res = await match_salesforce_account(
            company_name=row.get("company_name", ""),
            domain=row.get("domain"),
            sfdc_connection=sfdc_connection,
            threshold=threshold,
            scoring_backend=scoring_backend,
        )
        results.append(res)
    return results
