from __future__ import annotations
from typing import List, Optional, Literal
from enum import Enum
from pydantic import BaseModel, Field, field_validator, computed_field, ConfigDict

# ---------- Enums ----------

class Tone(Enum):
    INFORMATIVE = "informative"
    PERSUASIVE = "persuasive"
    HUMOROUS = "humorous"
    CONFRONTATIONAL = "confrontational"
    NEUTRAL = "neutral"
    EMOTIONAL = "emotional"

class Persuasion(Enum):
    ETHOS = "ethos"
    PATHOS = "pathos"
    LOGOS = "logos"

class EvidenceType(Enum):
    STATISTIC = "statistic"
    EXPERIMENT = "experiment"
    EXPERT_OPINION = "expert_opinion"
    ANECDOTE = "anecdote"
    DEMONSTRATION = "demonstration"
    DOCUMENT = "document"
    OTHER = "other"

class FallacyType(Enum):
    STRAWMAN = "strawman"
    AD_HOMINEM = "ad_hominem"
    FALSE_DILEMMA = "false_dilemma"
    SLIPPERY_SLOPE = "slippery_slope"
    APPEAL_TO_AUTHORITY = "appeal_to_authority"
    HASTY_GENERALIZATION = "hasty_generalization"
    WHATABOUTISM = "whataboutism"
    CIRCULAR_REASONING = "circular_reasoning"
    NONE = "none"

class ClaimStrength(Enum):
    WEAK = "weak"
    MODERATE = "moderate"
    STRONG = "strong"
    UNSUPPORTED = "unsupported"

class ImpactType(Enum):
    INFORM = "inform"
    PERSUADE = "persuade"
    INSPIRE = "inspire"
    MOBILIZE = "mobilize"
    ENTERTAIN = "entertain"
    MISLEAD = "mislead"

class ComplexityLevel(Enum):
    BEGINNER = "beginner"
    INTERMEDIATE = "intermediate"
    ADVANCED = "advanced"
    MIXED = "mixed"

# ---------- Small atoms ----------

class TimeSpan(BaseModel):
    start_s: float = Field(..., ge=0)
    end_s: float = Field(..., ge=0)

    @field_validator("end_s")
    @classmethod
    def _end_after_start(cls, v, info):  # type: ignore[override]
        start = info.data.get("start_s", 0)
        if v < start:
            raise ValueError("end_s must be >= start_s")
        return v

    @computed_field
    @property
    def duration_s(self) -> float:  # type: ignore[override]
        return self.end_s - self.start_s

class Reference(BaseModel):
    title: Optional[str] = None
    url: Optional[str] = None
    source_type: Optional[str] = None
    reliability_notes: Optional[str] = None

class Evidence(BaseModel):
    kind: EvidenceType
    text: str
    times: Optional[TimeSpan] = None
    references: List[Reference] = Field(default_factory=list)

class CounterArgument(BaseModel):
    text: str
    addressed: bool = False
    notes: Optional[str] = None

class Assumption(BaseModel):
    text: str
    explicit: bool = False

class FactCheck(BaseModel):
    status: Literal["unverified", "accurate", "false", "mixed"] = "unverified"
    notes: Optional[str] = None
    references: List[Reference] = Field(default_factory=list)

class Claim(BaseModel):
    text: str
    times: Optional[TimeSpan] = None
    strength: ClaimStrength = ClaimStrength.MODERATE
    evidences: List[Evidence] = Field(default_factory=list)
    assumptions: List[Assumption] = Field(default_factory=list)
    counterarguments: List[CounterArgument] = Field(default_factory=list)
    fact_check: FactCheck = Field(default_factory=FactCheck)

class RhetoricalDevice(BaseModel):
    name: str
    snippet: Optional[str] = None
    times: Optional[TimeSpan] = None

class Quote(BaseModel):
    text: str
    times: Optional[TimeSpan] = None
    who: Optional[str] = None
    why_memorable: Optional[str] = None

class Question(BaseModel):
    text: str
    times: Optional[TimeSpan] = None
    type: Optional[Literal["clarifying", "probing", "rhetorical", "open", "closed"]] = None
    suggested_answer: Optional[str] = None

class ActionItem(BaseModel):
    text: str
    times: Optional[TimeSpan] = None
    category: Optional[Literal["learn_more", "try_this", "buy", "subscribe", "share"]] = None

class GlossaryTerm(BaseModel):
    term: str
    definition: str
    difficulty: ComplexityLevel = ComplexityLevel.MIXED
    first_mentioned_s: Optional[float] = None

class Misconception(BaseModel):
    statement: str
    correction: str
    times: Optional[TimeSpan] = None

class InsightSegment(BaseModel):
    title: Optional[str] = None
    times: TimeSpan
    summary: Optional[str] = None
    key_points: List[str] = Field(default_factory=list)

class TimelineEvent(BaseModel):
    title: str
    times: TimeSpan
    detail: Optional[str] = None

# ---------- Metrics ----------

class TextMetrics(BaseModel):
    word_count: int = 0
    wpm: Optional[float] = None
    lexical_diversity: Optional[float] = Field(default=None, ge=0, le=1)
    sentiment: Optional[float] = Field(default=None, ge=-1, le=1)
    subjectivity: Optional[float] = Field(default=None, ge=0, le=1)

    @computed_field
    @property
    def density(self) -> Optional[float]:  # type: ignore[override]
        return self.wpm

# ---------- The Card ----------

class TranscriptInsightCard(BaseModel):
    model_config = ConfigDict(str_strip_whitespace=True)

    video_title: Optional[str] = None
    channel_name: Optional[str] = None
    duration_s: Optional[float] = Field(default=None, ge=0)

    dominant_tone: Tone = Tone.INFORMATIVE
    persuasion_modes: List[Persuasion] = Field(default_factory=list)
    fallacies: List[FallacyType] = Field(default_factory=list)
    complexity: ComplexityLevel = ComplexityLevel.MIXED
    impact: ImpactType = ImpactType.INFORM

    segments: List[InsightSegment] = Field(default_factory=list)
    timeline: List[TimelineEvent] = Field(default_factory=list)
    claims: List[Claim] = Field(default_factory=list)
    devices: List[RhetoricalDevice] = Field(default_factory=list)
    quotes: List[Quote] = Field(default_factory=list)
    questions: List[Question] = Field(default_factory=list)
    actions: List[ActionItem] = Field(default_factory=list)
    glossary: List[GlossaryTerm] = Field(default_factory=list)
    misconceptions: List[Misconception] = Field(default_factory=list)

    tl_dr: Optional[str] = None
    key_takeaways: List[str] = Field(default_factory=list)
    next_steps: List[str] = Field(default_factory=list)
    related_resources: List[Reference] = Field(default_factory=list)

    metrics: TextMetrics = Field(default_factory=TextMetrics)

    @computed_field
    @property
    def claim_count(self) -> int:  # type: ignore[override]
        return len(self.claims)

    @computed_field
    @property
    def evidence_count(self) -> int:  # type: ignore[override]
        return sum(len(c.evidences) for c in self.claims)

    @computed_field
    @property
    def ref_count(self) -> int:  # type: ignore[override]
        return sum(len(e.references) for c in self.claims for e in c.evidences) + len(self.related_resources)

    @computed_field
    @property
    def fallacy_flags(self) -> List[str]:  # type: ignore[override]
        return sorted(set(f.name for f in self.fallacies if f != FallacyType.NONE))

    def to_card(self) -> dict:
        header = {
            "title": self.video_title or "Transcript Insight Card",
            "subtitle": self.channel_name,
            "badges": [
                {"label": "Tone", "value": self.dominant_tone.value},
                {"label": "Complexity", "value": self.complexity.value},
                {"label": "Impact", "value": self.impact.value},
                {"label": "Claims", "value": self.claim_count},
                {"label": "Evidence", "value": self.evidence_count},
                {"label": "Refs", "value": self.ref_count},
            ] + ([{"label": "Fallacies", "value": ", ".join(self.fallacy_flags)}] if self.fallacy_flags else []),
        }
        sections = []
        if self.tl_dr or self.key_takeaways:
            sections.append({
                "title": "TL;DR & Key Takeaways",
                "items": ([self.tl_dr] if self.tl_dr else []) + self.key_takeaways,
            })
        if self.claims:
            sections.append({
                "title": "Claims & Evidence",
                "items": [
                    {
                        "claim": c.text,
                        "strength": c.strength.value,
                        "time": c.times.start_s if c.times else None,
                        "evidence": [
                            {
                                "kind": e.kind.value,
                                "text": e.text,
                                "time": e.times.start_s if e.times else None,
                                "refs": [{"title": r.title, "url": r.url} for r in e.references],
                            }
                            for e in c.evidences
                        ],
                        "assumptions": [a.text for a in c.assumptions],
                        "counterarguments": [{"text": ca.text, "addressed": ca.addressed} for ca in c.counterarguments],
                        "fact_check": {"status": c.fact_check.status, "notes": c.fact_check.notes},
                    }
                    for c in self.claims
                ],
            })
        if self.glossary:
            sections.append({
                "title": "Glossary",
                "items": [
                    {"term": g.term, "definition": g.definition, "difficulty": g.difficulty.value}
                    for g in self.glossary
                ],
            })
        if self.questions:
            sections.append({
                "title": "Questions to Ponder",
                "items": [
                    {"q": q.text, "type": q.type, "time": q.times.start_s if q.times else None}
                    for q in self.questions
                ],
            })
        if self.misconceptions:
            sections.append({
                "title": "Common Misconceptions",
                "items": [
                    {"statement": m.statement, "correction": m.correction}
                    for m in self.misconceptions
                ],
            })
        if self.actions or self.next_steps:
            sections.append({
                "title": "Next Steps",
                "items": self.next_steps + [a.text for a in self.actions],
            })
        if self.devices:
            sections.append({
                "title": "Rhetoric & Devices",
                "items": [
                    {
                        "name": d.name,
                        "time": d.times.start_s if d.times else None,
                        **({"snippet": d.snippet} if d.snippet else {}),
                    }
                    for d in self.devices
                ],
            })
        if self.segments:
            sections.append({
                "title": "Segments (Timeline)",
                "items": [
                    {
                        "title": s.title,
                        "start_s": s.times.start_s,
                        "end_s": s.times.end_s,
                        "summary": s.summary,
                        "key_points": s.key_points,
                    }
                    for s in self.segments
                ],
            })
        if self.metrics:
            sections.append({
                "title": "Text Metrics",
                "items": {
                    "word_count": self.metrics.word_count,
                    "wpm": self.metrics.wpm,
                    "lexical_diversity": self.metrics.lexical_diversity,
                    "sentiment": self.metrics.sentiment,
                    "subjectivity": self.metrics.subjectivity,
                },
            })
        if self.related_resources:
            sections.append({
                "title": "Related Resources",
                "items": [
                    {"title": r.title, "url": r.url, "type": r.source_type}
                    for r in self.related_resources
                ],
            })
        footer = {
            "persuasion_modes": [p.value for p in self.persuasion_modes],
            "devices": [
                {"name": d.name, "time": d.times.start_s if d.times else None}
                for d in self.devices
            ],
            "timeline": [
                {
                    "title": t.title,
                    "start_s": t.times.start_s,
                    "end_s": t.times.end_s,
                    "detail": t.detail,
                }
                for t in self.timeline
            ],
        }
        return {"header": header, "sections": sections, "footer": footer}

# --- Simple heuristic analyzer (placeholder) ---

def analyze_transcript(text: str, *, video_title: Optional[str] = None, channel_name: Optional[str] = None, duration_s: Optional[float] = None) -> TranscriptInsightCard:
    words = text.split()
    unique = len(set(words))
    # Simple segmentation by quarters if duration present
    segments: List[InsightSegment] = []
    if duration_s and duration_s > 0:
        quarter = duration_s / 4
        for i in range(4):
            start = i * quarter
            end = min(duration_s, (i + 1) * quarter)
            if start >= end:
                continue
            segments.append(
                InsightSegment(
                    title=f"Part {i+1}",
                    times=TimeSpan(start_s=start, end_s=end),
                    summary=f"Approx segment {i+1} covering transcript portion.",
                    key_points=[],
                )
            )
    metrics = TextMetrics(
        word_count=len(words),
        wpm=150.0 if duration_s else None,
        lexical_diversity=unique / max(1, len(words)),
    )
    card = TranscriptInsightCard(
        video_title=video_title,
        channel_name=channel_name,
        duration_s=duration_s,
        persuasion_modes=[Persuasion.LOGOS],
        fallacies=[FallacyType.NONE],
        complexity=ComplexityLevel.MIXED,
        impact=ImpactType.INFORM,
        tl_dr=("Transcript overview: " + " ".join(words[:25]) + ("..." if len(words) > 25 else "")) if words else None,
        key_takeaways=[],
        segments=segments,
        metrics=metrics,
    )
    return card
