"""Turn structured statement facts into natural-language answers."""

from __future__ import annotations

import json
import re
from typing import Any

from app.models.schemas import InsightTransaction, StructuredInsight

_PARAGRAPH_RE = re.compile(
    r"(?i)\b("
    r"in\s+a\s+paragraph|as\s+a\s+paragraph|paragraph\s+form|"
    r"as\s+prose|in\s+prose|in\s+words|write\s+it\s+out|"
    r"narrative|describe\s+in\s+words|full\s+sentence"
    r")\b"
)

_PROSE_RE = re.compile(
    r"(?i)\b("
    r"explain\s+naturally|natural\s+language|tell\s+me\s+in\s+words|"
    r"summarize\s+in\s+words|write\s+a\s+summary|plain\s+english|"
    r"analy[sz]e|overview|summar(?:y|ize)|what\s+do\s+you\s+think|"
    r"insights?|observations?"
    r")\b"
)


def wants_paragraph_format(question: str) -> bool:
    return bool(_PARAGRAPH_RE.search(question or ""))


def wants_prose_answer(question: str) -> bool:
    return bool(_PROSE_RE.search(question or ""))


def transactions_to_paragraph(
    transactions: list[InsightTransaction],
    *,
    intro: str | None = None,
) -> str:
    if not transactions:
        return intro or "I could not find any payments to list on this statement."
    parts: list[str] = []
    for t in transactions:
        desc = (t.description or "Payment").strip()
        amt = (t.amount or "").strip()
        if t.date:
            parts.append(f"on {t.date} you paid {amt} to {desc}")
        else:
            parts.append(f"you paid {amt} to {desc}")
    body = "; ".join(parts)
    if body and not body[0].isupper():
        body = body[0].upper() + body[1:]
    prefix = intro or f"Here are all {len(transactions)} payments on your statement:"
    return f"{prefix} {body}."


def apply_paragraph_style(insight: StructuredInsight, question: str) -> StructuredInsight:
    """Replace card-style transaction list with flowing prose."""
    txns = list(insight.transactions or [])
    if not txns:
        return insight
    prose = transactions_to_paragraph(txns, intro=insight.summary.rstrip(".") + ":" if insight.summary else None)
    return insight.model_copy(
        update={
            "summary": prose,
            "transactions": [],
            "footnotes": list(insight.footnotes or [])
            + [f"Listed all {len(txns)} payments in paragraph form as requested."],
        }
    )


def insight_facts_payload(insight: StructuredInsight) -> dict[str, Any]:
    return {
        "headline": insight.headline,
        "summary": insight.summary,
        "metrics": [{"label": m.label, "value": m.value} for m in (insight.metrics or [])],
        "highlights": list(insight.highlights or []),
        "payments": [
            {
                "date": t.date,
                "description": t.description,
                "amount": t.amount,
                "direction": t.direction,
            }
            for t in (insight.transactions or [])[:80]
        ],
        "footnotes": list(insight.footnotes or []),
    }


POLISH_PROMPT = """You are Alex, a friendly financial assistant. Rewrite the answer using ONLY the facts below.

Rules:
- Lead with a direct answer to the user's question in the first sentence.
- Then add 1–3 short supporting observations (patterns, largest items, what stands out).
- Do NOT restate every metric from the facts list — the UI already shows those in cards.
- Do not invent numbers, dates, merchants, or payments.
- Keep currency amounts exactly as shown when you do mention a figure.
- Friendly, concise, conversational. No filler. No greetings or goodbyes.
- Never mention RAG, chunks, vectors, extraction, or embeddings.

User question: {question}

Facts (JSON):
{facts}

Answer:"""


def polish_insight_with_llm(
    insight: StructuredInsight,
    question: str,
    *,
    invoke_ollama,
    max_payments_in_prompt: int = 25,
) -> str | None:
    """Optional LLM pass — grounds prose in structured facts, not raw chunks."""
    facts = insight_facts_payload(insight)
    payments = facts.get("payments") or []
    if len(payments) > max_payments_in_prompt:
        # Long lists are more reliable as deterministic prose.
        return None
    try:
        raw = invoke_ollama(
            POLISH_PROMPT.format(
                question=(question or "").strip(),
                facts=json.dumps(facts, ensure_ascii=False, indent=2),
            ),
            num_predict=220,
        )
    except Exception:
        return None
    text = (raw or "").strip()
    return text or None


def chat_text_from_insight(insight: StructuredInsight) -> str:
    """Copy/audit text: narrative summary first — cards already show metrics."""
    parts = [insight.summary.strip()] if insight.summary else []
    if insight.transactions:
        n = len(insight.transactions)
        parts.append("")
        parts.append(f"Payments ({n})" if n > 8 else "Payments")
        for t in insight.transactions:
            when = f"{t.date} · " if t.date else ""
            parts.append(f"- {when}{t.description} — {t.amount}")
    return "\n".join(parts).strip() or insight.headline
