"""Block-level style-exemplar extraction (channel B of Style Acquisition).

Splits one of Dafna's decisions into section→paragraph units, embeds them
(Voyage), and stores them in `style_exemplars` so the writer can retrieve real
block-level prose by section/outcome/practice_area (07-learning §0.2 channel B).

This is the SINGLE source of truth for exemplar extraction (G2): both the
one-time backfill (`scripts/backfill_style_exemplars.py`) and the live
final-enrollment path (`_enroll_final_in_library`) call `extract_and_store`.
Before this, exemplars were frozen at the seed backfill — new finals enrolled
into style_corpus but were never broken into exemplars, so the richest style
channel never grew. Embedding is Voyage-over-REST → container-safe.
"""
from __future__ import annotations

import logging

from legal_mcp.services import db, embeddings
from legal_mcp.services.chunker import _split_into_sections

logger = logging.getLogger(__name__)

# chunker section_type → style_exemplars.section
_SECTION_MAP = {
    "facts": "background",
    "appellant_claims": "claims",
    "respondent_claims": "claims",
    "legal_analysis": "discussion",
    "conclusion": "summary",
    "ruling": "summary",
    "intro": "other",
    "other": "other",
}

MIN_WORDS = 25      # skip tiny fragments
MAX_WORDS = 450     # skip over-long blobs (likely un-split)
MAX_PER_SECTION = 15


def _paragraphs(section_text: str) -> list[str]:
    """Split a section into paragraph units (blank-line separated; fall back to lines)."""
    raw = [p.strip() for p in section_text.split("\n\n")]
    if len(raw) <= 1:
        raw = [p.strip() for p in section_text.split("\n")]
    out = []
    for p in raw:
        wc = len(p.split())
        if MIN_WORDS <= wc <= MAX_WORDS:
            out.append(p)
    return out[:MAX_PER_SECTION]


def units_for(full_text: str) -> list[tuple[str, str]]:
    """(section, paragraph) units for a decision — pure, no I/O."""
    units: list[tuple[str, str]] = []
    for section_type, section_text in _split_into_sections(full_text or ""):
        section = _SECTION_MAP.get(section_type, "other")
        for para in _paragraphs(section_text):
            units.append((section, para))
    return units


async def extract_and_store(
    decision_number: str, source: str, full_text: str,
    practice_area: str = "", outcome: str = "",
) -> int:
    """Idempotently (re)build a decision's block-level style exemplars: split →
    embed → replace. Returns the number of exemplars stored. Raises on failure;
    callers in best-effort paths (enroll) should wrap in try/except."""
    units = units_for(full_text)
    if not units:
        return 0
    texts = [u[1] for u in units]
    vecs = await embeddings.embed_texts(texts, input_type="document")
    await db.delete_style_exemplars(decision_number, source)
    for (section, para), vec in zip(units, vecs):
        await db.insert_style_exemplar(
            decision_number=decision_number, source=source,
            practice_area=practice_area, outcome=outcome,
            section=section, paragraph_text=para, word_count=len(para.split()),
            embedding=vec,
        )
    return len(units)
