from __future__ import annotations
"""LLM-powered structured transcript insight extraction.

Produces richer fields for TranscriptInsightCard using a single multi-task prompt
to minimize round trips. Falls back gracefully if LLM disabled or provider
returns unusable output.
"""
from typing import Optional
import json
import re

from ..config import settings
from ..models.insight import (
    TranscriptInsightCard, Persuasion, FallacyType, ImpactType, Claim, ClaimStrength,
    Evidence, EvidenceType, Assumption, CounterArgument, FactCheck, GlossaryTerm,
    RhetoricalDevice, Question, Tone
)

SCHEMA_GUIDE = (
    "Return JSON with keys: tl_dr (string), key_takeaways (list of strings <=5), "
    "claims (list of claim objects), glossary (list), devices (list), questions (list), "
    "fallacies (list of enum names), persuasion_modes (list of enum names), impact (enum), tone (enum).\n"
    "Claim object: {text, strength (weak|moderate|strong|unsupported), evidences:[{kind, text}], assumptions:[{text}], counterarguments:[{text, addressed}], fact_check:{status}}.\n"
    "Glossary item: {term, definition, difficulty}. Device: {name, snippet}. Question: {text, type}."
)

def _truncate(text: str, max_chars: int) -> str:
    return text if len(text) <= max_chars else text[:max_chars] + "..."

def llm_analyze_transcript(text: str, *, video_title: Optional[str] = None, channel_name: Optional[str] = None) -> Optional[TranscriptInsightCard]:
    if not settings.llm_enabled:
        return None
    try:
        from .base import get_provider, LLMRequest  # type: ignore
    except Exception:  # pragma: no cover
        return None
    provider = get_provider(settings.llm_provider)
    if not provider:
        return None
    prompt = (
        "You analyze educational transcripts and extract structured insights. "
        "Follow the schema strictly and output a single JSON object ONLY.\n" + SCHEMA_GUIDE +
        "Transcript:\n" + _truncate(text, 12000)
    )
    try:
        resp = provider.complete(LLMRequest(prompt=prompt, temperature=0.1))
        raw = resp.text.strip()
    except Exception:
        return None
    if not raw:
        return None
    match = re.search(r"{[\s\S]+}", raw)
    raw_json = match.group(0) if match else raw
    try:
        data = json.loads(raw_json)
        if not isinstance(data, dict):
            return None
    except Exception:
        return None
    card = TranscriptInsightCard(
        video_title=video_title,
        channel_name=channel_name,
        tl_dr=data.get("tl_dr"),
        key_takeaways=[k for k in data.get("key_takeaways", []) if isinstance(k, str)][:5],
    )
    tone = (data.get("tone") or "informative").upper()
    card.dominant_tone = getattr(Tone, tone, Tone.INFORMATIVE)
    impact = (data.get("impact") or "inform").upper()
    card.impact = getattr(ImpactType, impact, ImpactType.INFORM)
    for p in data.get("persuasion_modes", []) or []:
        enum_name = str(p).upper()
        if hasattr(Persuasion, enum_name):
            card.persuasion_modes.append(getattr(Persuasion, enum_name))  # type: ignore[arg-type]
    for f in data.get("fallacies", []) or []:
        enum_name = str(f).upper()
        if hasattr(FallacyType, enum_name):
            card.fallacies.append(getattr(FallacyType, enum_name))  # type: ignore[arg-type]
    for c in data.get("claims", []) or []:
        if not isinstance(c, dict) or "text" not in c:
            continue
        strength = str(c.get("strength", "moderate")).upper()
        strength_enum = getattr(ClaimStrength, strength, ClaimStrength.MODERATE)
        evidences = []
        for e in c.get("evidences", []) or []:
            if not isinstance(e, dict) or "text" not in e:
                continue
            kind_raw = str(e.get("kind", "other")).upper()
            kind_enum = getattr(EvidenceType, kind_raw, EvidenceType.OTHER)
            evidences.append(Evidence(kind=kind_enum, text=e.get("text", "")))
        assumptions = [Assumption(text=a.get("text", "")) for a in c.get("assumptions", []) if isinstance(a, dict) and a.get("text")]
        counterarguments = [CounterArgument(text=ca.get("text", ""), addressed=bool(ca.get("addressed"))) for ca in c.get("counterarguments", []) if isinstance(ca, dict) and ca.get("text")]
        fact = c.get("fact_check") if isinstance(c.get("fact_check"), dict) else {}
        fc = FactCheck(status=fact.get("status", "unverified") if isinstance(fact, dict) else "unverified")
        card.claims.append(Claim(text=c["text"], strength=strength_enum, evidences=evidences, assumptions=assumptions, counterarguments=counterarguments, fact_check=fc))
    for g in data.get("glossary", []) or []:
        if not isinstance(g, dict) or "term" not in g or "definition" not in g:
            continue
        card.glossary.append(GlossaryTerm(term=g["term"], definition=g["definition"]))
    for d in data.get("devices", []) or []:
        if not isinstance(d, dict) or "name" not in d:
            continue
        card.devices.append(RhetoricalDevice(name=d["name"], snippet=d.get("snippet")))
    for q in data.get("questions", []) or []:
        if not isinstance(q, dict) or "text" not in q:
            continue
        card.questions.append(Question(text=q["text"], type=q.get("type")))
    return card
