from __future__ import annotations
from typing import Optional, Dict, Any, Tuple
from dataclasses import dataclass
from sqlmodel import Session, select
from .routing_pools import next_user
from ..model_defs.routing_models import RoutingRule, RoutingPool
from ..services.domain_family import get_registry, domain_normalize, DomainRelation


class CRMAdapter:
    def get_account_by_domain(self, domain: str) -> Optional[dict]:
        return None

    def get_owner_for_account(self, account_id: str) -> Optional[str]:
        return None


@dataclass
class LeadPayload:
    lead_id: Optional[str] = None
    email: Optional[str] = None
    company: Optional[str] = None
    state: Optional[str] = None
    country: Optional[str] = None
    employees: Optional[int] = None
    website: Optional[str] = None
    extra: Dict[str, Any] = None


def _infer_lead_domain(lead: LeadPayload) -> Optional[str]:
    # Existing email check
    if lead.email and "@" in lead.email:
        dom = lead.email.split("@")[-1].strip().lower()
        return domain_normalize(dom)

    # Existing website check
    if lead.website:
        return domain_normalize(lead.website)

    # NEW: Name fallback
    if lead.company:
        try:
            from .domain_family import get_registry

            reg = get_registry()
            family_id, confidence = reg.lookup_family_by_name(lead.company)
            if family_id and confidence >= 0.90:
                domains = list(reg.family_to_domains.get(family_id, []))
                # Return the parent domain (first one)
                if domains:
                    return domains[0]
        except Exception as e:
            # Log but don't crash - routing should continue without domain inference
            import logging

            logger = logging.getLogger(__name__)
            logger.debug(
                f"Failed to infer domain from company name '{lead.company}': {e}"
            )

    return None


def _account_match(
    session: Session, lead: LeadPayload, crm: Optional[CRMAdapter] = None
) -> Tuple[Optional[dict], Optional[str], Optional[str]]:
    reg = get_registry()
    lead_dom = _infer_lead_domain(lead)
    if not lead_dom:
        return None, None, None
    account = None
    if crm:
        account = crm.get_account_by_domain(lead_dom)
    if not account:
        return None, lead_dom, None
    acct_dom = domain_normalize(account.get("website") or account.get("domain"))
    rel, fam = reg.relation(lead_dom, acct_dom)
    return (
        (account if rel in (DomainRelation.EXACT, DomainRelation.FAMILY) else None),
        lead_dom,
        acct_dom,
    )


def _eval_condition(cond: Dict[str, Any], ctx: Dict[str, Any]) -> bool:
    if not cond:
        return True
    if "min_conf" in cond and ctx.get("account_confidence", 0.0) < float(
        cond["min_conf"]
    ):
        return False
    if "relation" in cond:
        rel = ctx.get("domain_relation")
        if rel not in set(cond["relation"]):
            return False
    abm = cond.get("abm_tier") or {}
    if "in" in abm and ctx.get("abm_tier") not in set(abm["in"]):
        return False
    cust = cond.get("customer_status") or {}
    if "in" in cust and ctx.get("customer_status") not in set(cust["in"]):
        return False
    geo = cond.get("geo") or {}
    if "state_in" in geo:
        st = (ctx.get("state") or "").upper()
        if st and st not in {s.upper() for s in geo["state_in"]}:
            return False
    if "country_in" in geo:
        co = (ctx.get("country") or "").upper()
        if co and co not in {c.upper() for c in geo["country_in"]}:
            return False
    size = cond.get("size") or {}
    emp = size.get("employees") or {}
    if "min" in emp and ctx.get("employees") is not None:
        if int(ctx.get("employees")) < int(emp["min"]):
            return False
    return True


def _apply_action(
    session: Session, action: Dict[str, Any], ctx: Dict[str, Any]
) -> Dict[str, Any]:
    if not action:
        return {"decision": "hold", "reason": "no_action"}
    kind = action.get("type")
    if kind == "assign_owner":
        return {
            "decision": "assign_owner",
            "owner_id": action.get("owner_id"),
            "reason": "assign_owner_rule",
        }
    if kind == "assign_pool":
        pool_id = action.get("pool_id")
        if not pool_id:
            return {"decision": "hold", "reason": "assign_pool_missing_id"}
        user = next_user(session, pool_id)
        if not user:
            return {
                "decision": "hold",
                "reason": "no_available_user",
                "pool_id": pool_id,
            }
        return {
            "decision": "assign_owner",
            "owner_id": user,
            "pool_id": str(pool_id),
            "reason": "pool_assignment",
        }
    if kind == "escalate_parent_owner":
        return {
            "decision": "assign_owner",
            "owner_id": ctx.get("parent_owner_id"),
            "reason": "escalated_parent",
        }
    if kind == "hold":
        return {"decision": "hold", "reason": action.get("reason") or "held"}
    return {"decision": "hold", "reason": f"unknown_action:{kind}"}


def route_lead(
    session: Session,
    lead: LeadPayload,
    tenant_id: Optional[str] = None,
    crm: Optional[CRMAdapter] = None,
) -> Dict[str, Any]:
    account, lead_dom, acct_dom = _account_match(session, lead, crm=crm)
    if account:
        owner = account.get("owner_id")
        if owner:
            return {
                "decision": "assign_owner",
                "owner_id": owner,
                "reason": "account_match_owner",
            }

    ctx = {
        "lead_id": lead.lead_id,
        "domain_relation": "NONE",
        "account_confidence": 0.0,
        "abm_tier": None,
        "customer_status": None,
        "state": lead.state,
        "country": lead.country,
        "employees": lead.employees,
    }
    rules = session.exec(
        select(RoutingRule)
        .where(
            (RoutingRule.tenant_id == tenant_id) | (RoutingRule.tenant_id.is_(None)),
            RoutingRule.enabled == True,
        )
        .order_by(RoutingRule.priority.asc())
    ).all()
    for rule in rules:
        if _eval_condition(rule.condition, ctx):
            decision = _apply_action(session, rule.action, ctx)
            decision["rule_id"] = str(rule.id)
            return decision

    pool = session.exec(
        select(RoutingPool).where(
            (RoutingPool.tenant_id == tenant_id) | (RoutingPool.tenant_id.is_(None)),
            RoutingPool.enabled == True,
        )
    ).first()
    if pool:
        user = next_user(session, pool.id)
        if user:
            return {
                "decision": "assign_owner",
                "owner_id": user,
                "pool_id": str(pool.id),
                "reason": "default_pool",
            }
    return {"decision": "hold", "reason": "no_rule_no_pool"}
