"""Structured call summary — bullet sections for Call Detail UI."""

from __future__ import annotations

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

STRUCTURED_SUMMARY_KEYS = (
    "points_to_discuss",
    "key_takeaways",
    "feature_tasks",
    "follow_up_tasks",
    "tasks",
)

STRUCTURED_SUMMARY_LABELS = {
    "points_to_discuss": "Point of Discuss",
    "key_takeaways": "Key Takeaway",
    "feature_tasks": "Feature Task",
    "follow_up_tasks": "Follow-up Task",
    "tasks": "Tasks",
}

STRUCTURED_SUMMARY_INSTRUCTIONS = """
STRUCTURED SUMMARY (structured_summary object — REQUIRED in every response; use transcript facts only):
- overview: 1-2 sentence call recap
- points_to_discuss: open topics, unanswered questions, or items to revisit (1-5 concise bullets when applicable)
- key_takeaways: main conclusions or insights from the call (REQUIRED: at least 1 bullet for substantive calls)
- feature_tasks: product, demo, configuration, or internal delivery tasks (1-5 concise bullets when applicable)
- follow_up_tasks: concrete next steps for agent or customer (REQUIRED: at least 1 bullet for substantive calls)
- tasks: other actionable tasks, commitments, or to-dos mentioned on the call (1-5 concise bullets when applicable)
Use empty arrays [] only when a section truly has nothing to list (e.g. wrong-number or silent call).
Do not invent facts not supported by the transcript.
Each point must appear in only one section — never repeat the same fact, task, or takeaway across multiple sections.
Within each section, every bullet must be distinct — do not include two bullets that say the same thing in different words.
"""

STRUCTURED_SUMMARY_JSON_TEMPLATE = """
  "structured_summary": {{
    "overview": "<optional 1-2 sentence recap>",
    "points_to_discuss": ["<bullet>"],
    "key_takeaways": ["<bullet>"],
    "feature_tasks": ["<bullet>"],
    "follow_up_tasks": ["<bullet>"],
    "tasks": ["<bullet>"]
  }},"""

_POINTS_PATTERNS = (
    "did not share",
    "did not mention",
    "did not provide",
    "did not discuss",
    "not share details",
    "not mentioned",
    "not covered",
    "unanswered",
    "remains unclear",
    "was not discussed",
    "no details about",
)

_FOLLOWUP_PATTERNS = (
    "scheduled",
    "follow up",
    "follow-up",
    "call back",
    "callback",
    "next step",
    "will send",
    "will share",
    "will provide",
    "demo for",
    "demo on",
    "demo tomorrow",
    "demo next",
)

_FEATURE_PATTERNS = (
    "introduced",
    "offered",
    "discussed services",
    "services like",
    "features like",
    "configuration",
    "integration",
    "auto dialer",
    "click to call",
    "ivr",
    "demo of",
)


def _clean_bullets(value: Any) -> List[str]:
    if not isinstance(value, list):
        if isinstance(value, str) and value.strip():
            return [value.strip()]
        return []
    items: List[str] = []
    for item in value:
        text = str(item or "").strip()
        if text:
            items.append(text)
    return items[:5]


def _word_set(text: str) -> set:
    return set(re.sub(r"[^a-z0-9 ]", "", text.lower()).split())


def _is_similar(a: str, b: str, threshold: float = 0.6) -> bool:
    wa, wb = _word_set(a), _word_set(b)
    if not wa or not wb:
        return False
    overlap = len(wa & wb) / min(len(wa), len(wb))
    return overlap >= threshold


def _dedupe_bullets(items: List[str]) -> List[str]:
    out: List[str] = []
    for item in items:
        if not item:
            continue
        if any(_is_similar(item, existing) for existing in out):
            continue
        out.append(item)
    return out[:5]


def _split_sentences(text: str) -> List[str]:
    parts = re.split(r"(?<=[.!?])\s+", text.strip())
    return [part.strip() for part in parts if part.strip()]


def _has_bullet_sections(structured: Dict[str, Any]) -> bool:
    return any(structured.get(key) for key in STRUCTURED_SUMMARY_KEYS)


def _is_empty_value(value: str) -> bool:
    cleaned = value.strip().lower()
    return cleaned in {"", "none", "n/a", "na", "not applicable"}


# Junk / non-actionable bullets (score leaks, placeholders) — hide in PCA + skip LSQ push.
_ACTIONABLE_TASK_JUNK_PATTERNS = (
    re.compile(r"^\s*no follow[- ]?up tasks?\b", re.I),
    re.compile(r"\bno follow[- ]?up tasks? identified\b", re.I),
    re.compile(r"^\s*no (feature )?tasks?\b", re.I),
    re.compile(r"\bnone identified\b", re.I),
    re.compile(r"\bnot applicable\b", re.I),
    re.compile(r"\bn/a\b", re.I),
    re.compile(r"\bscored\s*\d+\s*/\s*\d+\b", re.I),
    re.compile(r"\bquality\s*score\b", re.I),
    re.compile(r"\bparameter\s*(score|check|evaluation)\b", re.I),
    re.compile(r"^\s*none\.?\s*$", re.I),
    re.compile(r"^\s*n/?a\.?\s*$", re.I),
    re.compile(r"^\s*nil\.?\s*$", re.I),
    re.compile(r"^\s*-\s*$", re.I),
)


def is_actionable_task_bullet(text: Any) -> bool:
    """False for empty, tiny, or junk analytics bullets that should not show or push."""
    cleaned = " ".join(str(text or "").split()).strip()
    if len(cleaned) < 12:
        return False
    lower = cleaned.lower()
    placeholders = (
        "no follow-up",
        "no follow up",
        "no followup",
        "nothing to follow",
        "no action required",
        "no next step",
        "not identified",
        "no feature task",
        "no tasks identified",
    )
    if any(p in lower for p in placeholders) and len(cleaned) < 80:
        if re.search(r"^(no|none|n/a|nil)\b", lower) or "identified" in lower or "applicable" in lower:
            return False
    for pattern in _ACTIONABLE_TASK_JUNK_PATTERNS:
        if pattern.search(cleaned):
            return False
    return True


def filter_actionable_task_bullets(items: List[Any]) -> List[str]:
    """Drop junk bullets from feature/follow-up/task lists for UI and CRM push."""
    out: List[str] = []
    seen = set()
    for item in items or []:
        text = " ".join(str(item or "").split()).strip()
        if not text or not is_actionable_task_bullet(text):
            continue
        key = text.lower()
        if key in seen:
            continue
        seen.add(key)
        out.append(text)
    return out


def _extract_service_names(text: str) -> List[str]:
    match = re.search(
        r"services like (.+?)(?: and scheduled| and agreed| and confirmed|\.|$)",
        text,
        re.IGNORECASE,
    )
    if not match:
        return []
    chunk = match.group(1).strip()
    names = re.split(r",|\band\b", chunk)
    return [name.strip(" .") for name in names if name.strip(" .")]


def derive_structured_summary_from_legacy(ai_response: Dict[str, Any]) -> Optional[Dict[str, Any]]:
    """Build bullet sections from legacy flat analytics when structured_summary is missing."""
    overall = str(ai_response.get("overall_summary") or ai_response.get("summary") or "").strip()
    if not overall:
        return None

    points: List[str] = []
    takeaways: List[str] = []
    features: List[str] = []
    followups: List[str] = []
    tasks: List[str] = []

    call_purpose = str(ai_response.get("call_purpose") or "").strip()
    objections = str(ai_response.get("objections_concerns") or "").strip()

    if call_purpose and not _is_empty_value(call_purpose):
        takeaways.append(call_purpose.rstrip("."))

    if objections and not _is_empty_value(objections):
        points.append(objections.rstrip("."))

    for sentence in _split_sentences(overall):
        normalized = sentence.rstrip(".")
        lower = normalized.lower()

        if any(pattern in lower for pattern in _POINTS_PATTERNS):
            points.append(normalized)
            continue

        if " and scheduled " in lower:
            before, after = re.split(r"\band scheduled\b", normalized, maxsplit=1, flags=re.IGNORECASE)
            if before.strip():
                features.append(before.strip().rstrip("."))
            if after.strip():
                followups.append(f"Scheduled {after.strip()}")
            continue

        if any(pattern in lower for pattern in _FOLLOWUP_PATTERNS):
            followups.append(normalized)
            continue

        if any(pattern in lower for pattern in _FEATURE_PATTERNS):
            features.append(normalized)
            continue

        if any(
            phrase in lower
            for phrase in (
                "expressed interest",
                "mentioned having",
                "confirmed",
                "agreed",
                "customer from",
                "looking to",
                "team size",
                "salesperson",
            )
        ):
            takeaways.append(normalized)

    for name in _extract_service_names(overall):
        features.append(name)

    params = ai_response.get("parameters") or {}
    if isinstance(params, dict):
        for pdata in params.values():
            if not isinstance(pdata, dict):
                continue
            reasoning = str(pdata.get("reasoning") or "").strip()
            if reasoning and "follow" in reasoning.lower():
                followups.append(reasoning[:200].rstrip("."))

    structured = {
        "overview": overall,
        "points_to_discuss": _dedupe_bullets(points),
        "key_takeaways": _dedupe_bullets(takeaways),
        "feature_tasks": filter_actionable_task_bullets(_dedupe_bullets(features)),
        "follow_up_tasks": filter_actionable_task_bullets(_dedupe_bullets(followups)),
        "tasks": filter_actionable_task_bullets(_dedupe_bullets(tasks)),
    }

    if not _has_bullet_sections(structured):
        sentences = _split_sentences(overall)
        if len(sentences) > 1:
            structured["key_takeaways"] = _dedupe_bullets(
                [sentence.rstrip(".") for sentence in sentences[:3]]
            )

    return structured if _has_bullet_sections(structured) else None


def normalize_structured_summary(raw: Any) -> Optional[Dict[str, Any]]:
    if not isinstance(raw, dict):
        return None

    overview = str(raw.get("overview") or "").strip()
    normalized = {
        "overview": overview,
        "points_to_discuss": _dedupe_bullets(_clean_bullets(raw.get("points_to_discuss"))),
        "key_takeaways": _dedupe_bullets(_clean_bullets(raw.get("key_takeaways"))),
        "feature_tasks": filter_actionable_task_bullets(
            _dedupe_bullets(_clean_bullets(raw.get("feature_tasks")))
        ),
        "follow_up_tasks": filter_actionable_task_bullets(
            _dedupe_bullets(_clean_bullets(raw.get("follow_up_tasks")))
        ),
        "tasks": filter_actionable_task_bullets(
            _dedupe_bullets(_clean_bullets(raw.get("tasks")))
        ),
    }

    has_content = bool(overview) or any(normalized[key] for key in STRUCTURED_SUMMARY_KEYS)
    return normalized if has_content else None


def ensure_structured_summary(ai_response: Dict[str, Any]) -> Optional[Dict[str, Any]]:
    """Normalize LLM output or derive legacy bullets for UI display."""
    if not isinstance(ai_response, dict):
        return None

    overall = str(ai_response.get("overall_summary") or ai_response.get("summary") or "").strip()
    structured = normalize_structured_summary(ai_response.get("structured_summary"))

    if structured and _has_bullet_sections(structured):
        if not structured["overview"] and overall:
            structured["overview"] = overall
        ai_response["structured_summary"] = structured
        return structured

    derived = derive_structured_summary_from_legacy(ai_response)
    if derived:
        ai_response["structured_summary"] = derived
        return derived

    if structured:
        if not structured["overview"] and overall:
            structured["overview"] = overall
        ai_response["structured_summary"] = structured
        return structured

    if overall:
        fallback = {
            "overview": overall,
            "points_to_discuss": [],
            "key_takeaways": [],
            "feature_tasks": [],
            "follow_up_tasks": [],
            "tasks": [],
        }
        ai_response["structured_summary"] = fallback
        return fallback

    return None


def enrich_raw_response(raw_response: Any) -> Any:
    """Attach structured_summary to a parsed raw_response dict (non-destructive for other fields)."""
    if not isinstance(raw_response, dict):
        return raw_response
    ensure_structured_summary(raw_response)
    return raw_response


def flatten_structured_summary(structured: Dict[str, Any]) -> str:
    parts: List[str] = []
    overview = str(structured.get("overview") or "").strip()
    if overview:
        parts.append(overview)

    for key in STRUCTURED_SUMMARY_KEYS:
        bullets = structured.get(key) or []
        if not bullets:
            continue
        label = STRUCTURED_SUMMARY_LABELS.get(key, key)
        parts.append(f"{label}: " + "; ".join(str(b) for b in bullets))

    return "\n\n".join(parts)


def resolve_call_summary_text(ai_response: Dict[str, Any], *, call_summary_on: bool) -> str:
    """Normalize structured_summary on ai_response and return flat summary text for DB."""
    if not call_summary_on:
        return ""

    ensure_structured_summary(ai_response)

    text = str(ai_response.get("overall_summary") or "").strip()
    if not text:
        persisted = ai_response.get("structured_summary")
        if isinstance(persisted, dict):
            return flatten_structured_summary(persisted)
    return text
