"""Score sales propensity from transcript + call analytics context."""

from __future__ import annotations

import json
import re
from typing import Any, Dict, List, Optional, Tuple

from propensity_parameters_handler import PropensityParametersHandler


def _split_keywords(text: str) -> List[str]:
    if not text:
        return []
    parts = re.split(r"[\n,;|]+", str(text))
    return [p.strip().lower() for p in parts if p.strip()]


def _count_keyword_hits(haystack: str, keywords: List[str], max_hits: int) -> Tuple[int, List[str]]:
    hits = []
    if not haystack or not keywords:
        return 0, hits
    text = haystack.lower()
    for kw in keywords:
        if len(hits) >= max_hits:
            break
        if kw and kw in text:
            hits.append(kw)
    return len(hits), hits


def _propensity_band(score: float, hot_min: int = 75, warm_min: int = 45) -> str:
    if score >= hot_min:
        return "hot"
    if score >= warm_min:
        return "warm"
    return "cold"


def _context_boost(analytics_context: Dict[str, Any]) -> Dict[str, float]:
    boosts: Dict[str, float] = {}
    sentiment = str(analytics_context.get("sentiment") or "").lower()
    if "positive" in sentiment:
        boosts["_sentiment"] = 3.0
    elif "negative" in sentiment:
        boosts["_sentiment"] = -4.0

    purpose = str(analytics_context.get("call_purpose") or "").lower()
    if any(w in purpose for w in ("purchase", "booking", "demo", "visit", "quote")):
        boosts["_intent"] = 4.0

    objections = str(analytics_context.get("objections_concerns") or "").strip().lower()
    if objections and objections not in {"none", "n/a", "-"}:
        boosts["_objections"] = -3.0

    return boosts


def score_call_propensity(
    bid: str,
    transcript: str,
    analytics_context: Optional[Dict[str, Any]] = None,
    config: Optional[Any] = None,
    handler: Optional[PropensityParametersHandler] = None,
) -> Optional[Dict[str, Any]]:
    """Return propensity scoring payload or None if no parameters configured."""
    analytics_context = analytics_context or {}
    handler = handler or PropensityParametersHandler(config or {})
    handler.ensure_table()
    parameters = handler.get_parameters(bid)
    if not parameters:
        return None

    transcript_text = str(transcript or "")
    summary_text = str(analytics_context.get("summary") or "")
    combined_text = f"{transcript_text}\n{summary_text}"

    parameter_scores: Dict[str, Any] = {}
    parameter_detections: Dict[str, Any] = {}
    total_earned = 0.0
    total_possible = 0.0

    for param in parameters:
        name = param.get("parameter_name") or "Parameter"
        max_score = max(1, int(param.get("max_score") or 10))
        max_hits = max(1, int(param.get("max_keyword_hits") or 3))
        pos_kw = _split_keywords(param.get("positive_keywords") or "")
        neg_kw = _split_keywords(param.get("negative_keywords") or "")
        pos_pts = int(param.get("positive_points_per_hit") or 5)
        neg_pts = int(param.get("negative_points_per_hit") or 5)

        pos_count, pos_hits = _count_keyword_hits(combined_text, pos_kw, max_hits)
        neg_count, neg_hits = _count_keyword_hits(combined_text, neg_kw, max_hits)

        earned = min(max_score, max(0, (pos_count * pos_pts) - (neg_count * neg_pts)))

        mode = str(param.get("detection_mode") or "keyword").lower()
        if mode == "hybrid":
            boosts = _context_boost(analytics_context)
            earned = min(max_score, earned + sum(boosts.values()))

        parameter_scores[name] = {
            "score": int(round(earned)),
            "max_score": max_score,
            "applicable": True,
        }
        parameter_detections[name] = {
            "positive_hits": pos_hits,
            "negative_hits": neg_hits,
            "reasoning": (
                f"Matched {pos_count} positive and {neg_count} negative keyword(s) for {name}."
            ),
        }
        total_earned += earned
        total_possible += max_score

    if total_possible <= 0:
        return None

    propensity_score = int(round((total_earned / total_possible) * 100))
    propensity_score = max(0, min(100, propensity_score))

    return {
        "propensity_score": propensity_score,
        "propensity_band": _propensity_band(propensity_score),
        "propensity_parameter_scores": json.dumps(parameter_scores),
        "propensity_parameter_detections": json.dumps(parameter_detections),
        "propensity_total_possible": int(total_possible),
        "propensity_model": "keyword_v1",
    }
