"""MCP tools for decision drafting support."""

from __future__ import annotations

import json
from pathlib import Path
from uuid import UUID

from legal_mcp import config
from legal_mcp.services import audit, db, embeddings, git_sync, research_md
from legal_mcp.services.lessons import (
    CITATION_GUIDANCE,
    DECISION_TEMPLATES,
    DISCUSSION_RULES,
    GOLDEN_RATIOS,
    OPENING_STRATEGIES,
    PARAGRAPH_LENGTHS,
    SUMMARY_STRATEGIES,
    TRANSITION_PHRASES,
    VALID_OUTCOMES,
    format_ratios_comment,
    get_lessons_for_outcome,
)

# Fallback template for cases without expected_outcome
DECISION_TEMPLATE = """# החלטה

## בפני: דפנה תמיר, יו"ר ועדת הערר מחוז ירושלים

**ערר מספר:** {case_number}
**נושא:** {subject}
**העוררים:** {appellants}
**המשיבים:** {respondents}
**כתובת הנכס:** {property_address}

---

## א. רקע עובדתי

[תיאור הרקע העובדתי של הערר]

## ב. טענות העוררים

[סיכום טענות העוררים]

## ג. טענות המשיבים

[סיכום טענות המשיבים]

## ד. דיון והכרעה

[ניתוח משפטי]

## ה. מסקנה

[מסקנת הוועדה]

## ו. החלטה

[ההחלטה הסופית]

---
ניתנה היום, {date}
דפנה תמיר, יו"ר ועדת הערר
"""


async def get_style_guide() -> str:
    """שליפת דפוסי הסגנון של דפנה - נוסחאות, ביטויים אופייניים ומבנה, כולל לקחים מעשיים."""
    patterns = await db.get_style_patterns()

    result = "# מדריך סגנון - דפנה תמיר\n\n"

    # Part 1: DB-sourced patterns (from analyze_style)
    if patterns:
        type_names = {
            "opening_formula": "נוסחאות פתיחה",
            "transition": "ביטויי מעבר",
            "citation_style": "סגנון ציטוט",
            "analysis_structure": "מבנה ניתוח",
            "closing_formula": "נוסחאות סיום",
            "characteristic_phrase": "ביטויים אופייניים",
            "argument_flow": "זרימת טיעון",
            "evidence_handling": "התייחסות לראיות",
        }

        grouped: dict[str, list] = {}
        for p in patterns:
            pt = p["pattern_type"]
            if pt not in grouped:
                grouped[pt] = []
            grouped[pt].append({
                "text": p["pattern_text"],
                "context": p["context"],
                "frequency": p["frequency"],
            })

        for ptype, items in grouped.items():
            result += f"## {type_names.get(ptype, ptype)}\n\n"
            for item in items:
                result += f"- **{item['text']}** ({item['context']}, תדירות: {item['frequency']})\n"
            result += "\n"
    else:
        result += "_לא נמצאו דפוסים מקורפוס. יש להעלות החלטות ולהריץ /style-report._\n\n"

    # Part 2: Lessons-based guidance
    result += "---\n\n# לקחים מעשיים\n\n"

    # Universal discussion rules
    result += "## כללי דיון אוניברסליים\n\n"
    for rule in DISCUSSION_RULES["universal"]:
        result += f"- {rule}\n"
    result += "\n"

    # Citation technique
    result += "## טכניקת ציטוט\n\n"
    result += f"{CITATION_GUIDANCE}\n\n"

    # Transition phrases
    result += "## ביטויי מעבר (מהשוואת טיוטות)\n\n"
    for p in TRANSITION_PHRASES:
        ctx = p["context"]
        outcome_note = f" [בעיקר ב-{p['outcome']}]" if p["outcome"] else ""
        result += f"- **{p['phrase']}** — {ctx}{outcome_note}\n"
    result += "\n"

    # Paragraph lengths
    result += "## אורכי פסקאות מומלצים\n\n"
    result += "| סוג | מילים |\n|-----|------|\n"
    labels = {
        "claims": "טענות",
        "discussion_regular": "דיון רגיל",
        "discussion_with_citation": "דיון + ציטוט",
        "discussion_average": "ממוצע דיון",
    }
    for key, (lo, hi) in PARAGRAPH_LENGTHS.items():
        result += f"| {labels.get(key, key)} | {lo}-{hi} |\n"
    result += "\n"

    # Golden ratios
    result += "## יחסי הזהב (אחוזי סעיפים מסך ההחלטה)\n\n"
    result += "| סוג ערר | רקע | טענות | דיון | סיכום |\n"
    result += "|---------|------|-------|------|-------|\n"
    outcome_labels = {
        "rejection": "רישוי נדחה",
        "full_acceptance": "רישוי מתקבל",
        "partial_acceptance": "רישוי קבלה חלקית",
        "betterment_levy": "היטל השבחה",
    }
    for outcome in VALID_OUTCOMES:
        r = GOLDEN_RATIOS[outcome]
        result += (
            f"| {outcome_labels[outcome]} "
            f"| {r['background'][0]}-{r['background'][1]}% "
            f"| {r['claims'][0]}-{r['claims'][1]}% "
            f"| {r['discussion'][0]}-{r['discussion'][1]}% "
            f"| {r['summary'][0]}-{r['summary'][1]}% |\n"
        )
    result += "\n"

    # Opening and summary strategies
    result += "## אסטרטגיות פתיחה וסיכום לפי תוצאה\n\n"
    for outcome in VALID_OUTCOMES:
        opening = OPENING_STRATEGIES[outcome]
        summary = SUMMARY_STRATEGIES[outcome]
        result += f"### {outcome_labels[outcome]}\n"
        result += f"- **פתיחה:** {opening['description']} ({opening['paragraphs'][0]}-{opening['paragraphs'][1]} פסקאות)\n"
        result += f"- **סיכום ({summary['heading']}):** {summary['description']}\n\n"

    return result


async def draft_section(
    case_number: str,
    section: str,
    instructions: str = "",
) -> str:
    """הרכבת הקשר מלא לניסוח סעיף בהחלטה - כולל עובדות מהמסמכים, תקדימים רלוונטיים ודפוסי סגנון.

    Args:
        case_number: מספר תיק הערר
        section: סוג הסעיף (facts, appellant_claims, respondent_claims, legal_analysis, conclusion, ruling)
        instructions: הנחיות נוספות לניסוח
    """
    case = await db.get_case_by_number(case_number)
    if not case:
        return f"תיק {case_number} לא נמצא."

    case_id = UUID(case["id"])
    expected_outcome = case.get("expected_outcome", "")

    # 1. Get relevant chunks from case documents
    section_query = {
        "facts": "רקע עובדתי של התיק",
        "appellant_claims": "טענות העוררים",
        "respondent_claims": "טענות המשיבים",
        "legal_analysis": "ניתוח משפטי ודיון",
        "conclusion": "מסקנות",
        "ruling": "החלטה",
    }.get(section, section)

    query_emb = await embeddings.embed_query(section_query)
    case_chunks = await db.search_similar(
        query_embedding=query_emb, limit=10, case_id=case_id
    )

    # 2. Get similar sections from precedents
    precedent_chunks = await db.search_similar(
        query_embedding=query_emb, limit=5, section_type=section
    )
    # Filter out chunks from the same case
    precedent_chunks = [c for c in precedent_chunks if str(c["case_id"]) != case["id"]]

    # 3. Get style patterns
    style_patterns = await db.get_style_patterns()

    # Build context
    context = {
        "case": {
            "case_number": case["case_number"],
            "title": case["title"],
            "appellants": case["appellants"],
            "respondents": case["respondents"],
            "subject": case["subject"],
            "property_address": case["property_address"],
            "expected_outcome": expected_outcome,
        },
        "section": section,
        "instructions": instructions,
        "case_documents": [
            {
                "document": c["document_title"],
                "section_type": c["section_type"],
                "content": c["content"],
            }
            for c in case_chunks
        ],
        "precedents": [
            {
                "case_number": c["case_number"],
                "document": c["document_title"],
                "content": c["content"][:500],
            }
            for c in precedent_chunks[:3]
        ],
        "style_patterns": [
            {
                "type": p["pattern_type"],
                "text": p["pattern_text"],
            }
            for p in style_patterns[:15]
        ],
    }

    # 4. Add outcome-aware drafting guidance
    if expected_outcome and expected_outcome in VALID_OUTCOMES:
        lessons = get_lessons_for_outcome(expected_outcome)
        guidance: dict = {
            "outcome": expected_outcome,
            "golden_ratios": lessons["golden_ratios"],
            "citation_guidance": lessons["citation_guidance"],
        }

        # Section-specific guidance
        if section == "legal_analysis":
            guidance["discussion_rules"] = lessons["discussion_rules"]
            guidance["opening_strategy"] = lessons["opening_strategy"]
            guidance["transition_phrases"] = lessons["transition_phrases"]
            guidance["paragraph_lengths"] = lessons["paragraph_lengths"]
        elif section in ("conclusion", "ruling"):
            guidance["summary_strategy"] = lessons["summary_strategy"]
        elif section == "facts":
            guidance["target_ratio"] = lessons["golden_ratios"].get("background", "")
            guidance["paragraph_lengths"] = {"claims": lessons["paragraph_lengths"].get("claims", "")}
        elif section in ("appellant_claims", "respondent_claims"):
            guidance["target_ratio"] = lessons["golden_ratios"].get("claims", "")
            guidance["paragraph_lengths"] = {"claims": lessons["paragraph_lengths"].get("claims", "")}

        context["drafting_guidance"] = guidance

    return json.dumps(context, ensure_ascii=False, indent=2)


async def get_chair_directions(case_number: str) -> str:
    """שליפת עמדות יו"ר הוועדה (דפנה) על סוגיות הערר, לצורך יצירת direction_doc
    לכותב. קורא מ-analysis-and-research.md (שנוצר ע"י legal-analyst ומולא ע"י
    דפנה דרך ה-UI).

    מחזיר JSON עם סטטוס, כמה סוגיות מולאו וכמה עדיין ריקות, ורשימה של עמדות
    מובנות — ניתן להזריק ישירות כ-direction_doc לבלוק י (דיון) ולבלוק יא (סיכום).

    סטטוסים:
      missing  — הקובץ לא קיים
      empty    — הקובץ קיים אבל כל העמדות ריקות (טרם נקבעה דעה)
      partial  — חלק מהעמדות מולאו
      complete — כל העמדות מולאו

    אם המצב הוא `empty` או `missing` — הכותב צריך לעצור ולבקש מדפנה למלא
    את הקובץ דרך ה-UI לפני המשך הכתיבה.

    Args:
        case_number: מספר תיק הערר
    """
    case_dir = config.find_case_dir(case_number)
    file_path = case_dir / "documents" / "research" / "analysis-and-research.md"
    result = research_md.extract_chair_directions(file_path)
    return json.dumps(result, ensure_ascii=False, indent=2)


async def get_decision_template(case_number: str) -> str:
    """קבלת תבנית מבנית להחלטה מלאה עם פרטי התיק, מותאמת לסוג התוצאה הצפויה.

    Args:
        case_number: מספר תיק הערר
    """
    from datetime import date

    case = await db.get_case_by_number(case_number)
    if not case:
        return f"תיק {case_number} לא נמצא."

    expected_outcome = case.get("expected_outcome", "")

    format_args = dict(
        case_number=case["case_number"],
        subject=case["subject"],
        appellants=", ".join(case.get("appellants", [])),
        respondents=", ".join(case.get("respondents", [])),
        property_address=case.get("property_address", ""),
        date=date.today().strftime("%d.%m.%Y"),
    )

    # Use outcome-specific template if available
    if expected_outcome and expected_outcome in DECISION_TEMPLATES:
        # Add ratio comments
        format_args["ratios_background"] = format_ratios_comment(expected_outcome, "background")
        format_args["ratios_claims"] = format_ratios_comment(expected_outcome, "claims")
        format_args["ratios_discussion"] = format_ratios_comment(expected_outcome, "discussion")
        format_args["ratios_summary"] = format_ratios_comment(expected_outcome, "summary")

        template = DECISION_TEMPLATES[expected_outcome].format(**format_args)

        # Add guidance header
        opening = OPENING_STRATEGIES[expected_outcome]
        summary = SUMMARY_STRATEGIES[expected_outcome]
        header = (
            f"<!-- תבנית מותאמת ל: {expected_outcome} -->\n"
            f"<!-- פתיחת דיון: {opening['description']} -->\n"
            f"<!-- סיכום: {summary['description']} -->\n\n"
        )
        return header + template
    else:
        # Fallback to generic template
        template = DECISION_TEMPLATE.format(**format_args)
        if not expected_outcome:
            template = (
                "<!-- לא הוגדרה תוצאה צפויה. הגדר expected_outcome בתיק לקבלת תבנית מותאמת. -->\n"
                f"<!-- ערכים אפשריים: {', '.join(VALID_OUTCOMES)} -->\n\n"
            ) + template
        return template


async def validate_decision(case_number: str) -> str:
    """בדיקת QA אוטומטית על ההחלטה — 6 בדיקות. אם נכשלת בדיקה קריטית — ייצוא חסום.

    Args:
        case_number: מספר תיק הערר
    """
    from legal_mcp.services import qa_validator

    case = await db.get_case_by_number(case_number)
    if not case:
        return f"תיק {case_number} לא נמצא."

    case_id = UUID(case["id"])

    try:
        result = await qa_validator.validate_decision(case_id)
        return json.dumps(result, default=str, ensure_ascii=False, indent=2)
    except ValueError as e:
        return json.dumps({"status": "error", "message": str(e)}, ensure_ascii=False, indent=2)


async def export_docx(case_number: str, output_path: str = "") -> str:
    """ייצוא החלטה לקובץ DOCX מעוצב — גופן David, RTL, כותרות, מספור סעיפים.

    הקובץ נוצר עם bookmarks ב-12 הבלוקים (אנקורים ל-revisions עתידיים),
    ומסומן כ-active_draft_path של התיק.

    Args:
        case_number: מספר תיק הערר
        output_path: נתיב לשמירה (אופציונלי — ברירת מחדל: תיקיית התיק)
    """
    from legal_mcp.services import docx_exporter

    case = await db.get_case_by_number(case_number)
    if not case:
        return f"תיק {case_number} לא נמצא."

    case_id = UUID(case["id"])

    # INV-EX3 / INV-QA3: a decision cannot be exported while critical QA gates
    # fail (or before QA has been run at all). Gate on the STORED qa_results —
    # cheap SELECT, no LLM re-run.
    if not await db.qa_run_exists(case_id):
        return json.dumps({
            "status": "error",
            "message": "ייצוא נחסם: בקרת איכות (QA) טרם רצה על התיק. "
                       "הרץ validate_decision לפני ייצוא.",
        }, ensure_ascii=False, indent=2)

    critical = await db.get_critical_qa_failures(case_id)
    if critical:
        gate_names = ", ".join(r["check_name"] for r in critical)
        return json.dumps({
            "status": "error",
            "message": f"ייצוא נחסם: שערי QA קריטיים נכשלו ({gate_names}). "
                       f"תקן את הליקויים והרץ validate_decision מחדש לפני ייצוא.",
            "failed_gates": [r["check_name"] for r in critical],
        }, ensure_ascii=False, indent=2)

    try:
        path = await docx_exporter.export_decision(case_id, output_path or None)
        # Register this export as the new source of truth
        await db.set_active_draft_path(case_id, path)
        await audit.log_action_safe(
            "export_docx", case_id=case_id,
            details={"path": str(path)},
        )
        await db.mark_blocks_stale(case_id, False)
        case_dir = config.find_case_dir(case_number)
        if case_dir.exists():
            git_sync.commit_and_push(case_dir, f"ייצוא DOCX: {Path(path).name}")
        return json.dumps({
            "status": "completed",
            "path": path,
            "active_draft_path": path,
            "message": f"DOCX נוצר: {path}",
        }, ensure_ascii=False, indent=2)
    except ValueError as e:
        return json.dumps({
            "status": "error",
            "message": str(e),
        }, ensure_ascii=False, indent=2)


# ── Interim draft (pre-ruling) ────────────────────────────────────

# Blocks written for the interim draft, in display order.
# This is the same content the chair sees in the final decision (same template,
# same skill, same prompts) — minus opening, ruling, summary, signatures.
_INTERIM_BLOCKS = ["block-he", "block-vav", "block-tet", "block-zayin", "block-chet"]


async def extract_appraiser_facts(case_number: str) -> str:
    """חילוץ תכניות והיתרים מכל השומות בתיק וזיהוי סתירות בין שמאים.

    משמש כהכנה לטיוטת ביניים: בלוק ט (תכניות חלות) זקוק לעובדות מובנות
    כדי לפרט תת-פרק היתרים ולסמן סתירות בנוסח ניטרלי.

    Args:
        case_number: מספר תיק הערר
    """
    from legal_mcp.services import appraiser_facts_extractor

    case = await db.get_case_by_number(case_number)
    if not case:
        return json.dumps({"status": "error",
                           "message": f"תיק {case_number} לא נמצא."},
                          ensure_ascii=False, indent=2)
    case_id = UUID(case["id"])
    try:
        result = await appraiser_facts_extractor.extract_appraiser_facts(case_id)
        return json.dumps(result, default=str, ensure_ascii=False, indent=2)
    except Exception as e:
        return json.dumps({"status": "error", "message": str(e)},
                          ensure_ascii=False, indent=2)


async def write_interim_draft(case_number: str, instructions: str = "") -> str:
    """כתיבת ארבעת הבלוקים לטיוטת ביניים: רקע (ו), תכניות+היתרים (ט),
    טענות הצדדים (ז), הליכים (ח). אם לא חולצו עובדות שמאיות עדיין —
    מריץ extract_appraiser_facts קודם כדי שבלוק ט יקבל פרק היתרים תקף.

    הבלוקים נכתבים באותו skill, אותם prompts ואותו טמפלט כמו בטיוטה רגילה —
    הסדר משתנה רק בעת הייצוא ל-DOCX (ראה export_interim_draft).

    Args:
        case_number: מספר תיק הערר
        instructions: הנחיות נוספות (לכל הבלוקים)
    """
    from legal_mcp.services import appraiser_facts_extractor, block_writer

    case = await db.get_case_by_number(case_number)
    if not case:
        return json.dumps({"status": "error",
                           "message": f"תיק {case_number} לא נמצא."},
                          ensure_ascii=False, indent=2)
    case_id = UUID(case["id"])

    # Make sure appraiser facts exist before writing block-tet (which depends on them).
    facts = await db.list_appraiser_facts(case_id)
    facts_run: dict | None = None
    if not facts:
        try:
            facts_run = await appraiser_facts_extractor.extract_appraiser_facts(case_id)
        except Exception as e:
            facts_run = {"status": "error", "message": str(e)}

    results = []
    for bid in _INTERIM_BLOCKS:
        try:
            r = await block_writer.write_and_store_block(case_id, bid, instructions)
            results.append({
                "block_id": bid,
                "title": r["title"],
                "word_count": r["word_count"],
                "status": "completed",
            })
        except Exception as e:
            results.append({
                "block_id": bid,
                "status": "error",
                "error": str(e),
            })

    return json.dumps({
        "status": "completed",
        "blocks": results,
        "appraiser_facts_run": facts_run,
        "total_words": sum(r.get("word_count", 0) for r in results),
        "completed": sum(1 for r in results if r["status"] == "completed"),
    }, default=str, ensure_ascii=False, indent=2)


async def export_interim_draft(case_number: str, output_path: str = "") -> str:
    """ייצוא טיוטת ביניים ל-DOCX — אותו עיצוב של טיוטה רגילה (David, RTL,
    bookmarks), אבל בסדר חדש: רקע → תכניות+היתרים → טענות → הליכים, ללא
    דיון/סיכום/חתימות. שם הקובץ: טיוטת-ביניים-v{N}.docx.

    Args:
        case_number: מספר תיק הערר
        output_path: נתיב לשמירה (אופציונלי)
    """
    from legal_mcp.services import docx_exporter

    case = await db.get_case_by_number(case_number)
    if not case:
        return json.dumps({"status": "error",
                           "message": f"תיק {case_number} לא נמצא."},
                          ensure_ascii=False, indent=2)
    case_id = UUID(case["id"])

    try:
        path = await docx_exporter.export_decision(
            case_id, output_path or None, mode="interim",
        )
        await db.set_active_draft_path(case_id, path)
        case_dir = config.find_case_dir(case_number)
        if case_dir.exists():
            git_sync.commit_and_push(case_dir, f"טיוטת ביניים: {Path(path).name}")
        return json.dumps({
            "status": "completed",
            "mode": "interim",
            "path": path,
            "active_draft_path": path,
            "message": f"טיוטת ביניים נוצרה: {path}",
        }, ensure_ascii=False, indent=2)
    except ValueError as e:
        return json.dumps({"status": "error", "message": str(e)},
                          ensure_ascii=False, indent=2)


async def apply_user_edit(case_number: str, edit_filename: str) -> str:
    """רישום עריכה שהעלה המשתמש כמקור האמת החדש של התיק.

    התהליך:
    1. מאתר את הקובץ `עריכה-v*.docx` בתיקיית ה-exports
    2. מזריק bookmarks רטרואקטיבית (אם אין) דרך docx_retrofit
    3. מעדכן את cases.active_draft_path

    Args:
        case_number: מספר תיק הערר
        edit_filename: שם הקובץ (למשל "עריכה-v1.docx") או נתיב מלא
    """
    from legal_mcp.services import docx_retrofit

    case = await db.get_case_by_number(case_number)
    if not case:
        return json.dumps({"status": "error",
                           "message": f"תיק {case_number} לא נמצא."},
                          ensure_ascii=False, indent=2)

    case_id = UUID(case["id"])
    export_dir = config.find_case_dir(case_number) / "exports"
    edit_path = export_dir / edit_filename if "/" not in edit_filename else Path(edit_filename)
    if not edit_path.exists():
        return json.dumps({"status": "error",
                           "message": f"קובץ לא נמצא: {edit_path}"},
                          ensure_ascii=False, indent=2)

    try:
        retrofit_result = docx_retrofit.retrofit_bookmarks(edit_path)
        await db.set_active_draft_path(case_id, str(edit_path))
        await db.mark_blocks_stale(case_id, True)
        case_dir = config.find_case_dir(case_number)
        if case_dir.exists():
            git_sync.commit_and_push(case_dir, f"גרסת עריכה: {edit_path.name}")
        return json.dumps({
            "status": "completed",
            "active_draft_path": str(edit_path),
            "bookmarks_added": retrofit_result.get("bookmarks_added", []),
            "missing_blocks": retrofit_result.get("missing_blocks", []),
            "structural_fallback": retrofit_result.get("structural_fallback", []),
            "existing_bookmarks": retrofit_result.get("existing_bookmarks", []),
        }, ensure_ascii=False, indent=2)
    except Exception as e:
        return json.dumps({"status": "error", "message": str(e)},
                          ensure_ascii=False, indent=2)


async def list_bookmarks(case_number: str) -> str:
    """רשימת bookmarks הקיימים ב-active_draft של התיק.

    משמש לסוכנים כדי לדעת אילו אנקורים זמינים לפני שליחת revisions.
    """
    from legal_mcp.services import docx_reviser

    case = await db.get_case_by_number(case_number)
    if not case:
        return json.dumps({"status": "error",
                           "message": f"תיק {case_number} לא נמצא."},
                          ensure_ascii=False, indent=2)

    active_path = await db.get_active_draft_path(UUID(case["id"]))
    if not active_path or not Path(active_path).exists():
        return json.dumps({"status": "no_active_draft",
                           "message": "לא נמצא active_draft. הרץ ייצוא או העלה עריכה."},
                          ensure_ascii=False, indent=2)

    try:
        names = docx_reviser.list_bookmarks(active_path)
        return json.dumps({
            "status": "completed",
            "active_draft_path": active_path,
            "bookmarks": names,
        }, ensure_ascii=False, indent=2)
    except Exception as e:
        return json.dumps({"status": "error", "message": str(e)},
                          ensure_ascii=False, indent=2)


async def revise_draft(case_number: str, revisions_json: str,
                       author: str = "מערכת AI") -> str:
    """החלת revisions מסומנים כ-Track Changes על ה-active_draft של התיק.

    יוצר קובץ חדש `טיוטה-v{N+1}.docx` (מגרסה הבאה בתור), ומעדכן את
    active_draft_path אליו.

    Args:
        case_number: מספר תיק הערר
        revisions_json: JSON string של array עם אובייקטים:
            [{"id": "r1", "type": "insert_after"|"insert_before"|"replace"|"delete",
              "anchor_bookmark": "block-yod", "content": "...", "style": "body"|"heading"|"quote",
              "reason": "..."}, ...]
        author: מחרוזת המחבר שתופיע ב-Track Changes
    """
    from legal_mcp.services import docx_reviser

    case = await db.get_case_by_number(case_number)
    if not case:
        return json.dumps({"status": "error",
                           "message": f"תיק {case_number} לא נמצא."},
                          ensure_ascii=False, indent=2)

    case_id = UUID(case["id"])
    active_path = await db.get_active_draft_path(case_id)
    if not active_path or not Path(active_path).exists():
        return json.dumps({"status": "error",
                           "message": "אין active_draft. הרץ ייצוא או apply_user_edit קודם."},
                          ensure_ascii=False, indent=2)

    try:
        raw = json.loads(revisions_json) if isinstance(revisions_json, str) else revisions_json
    except json.JSONDecodeError as e:
        return json.dumps({"status": "error", "message": f"JSON לא תקף: {e}"},
                          ensure_ascii=False, indent=2)

    revisions = []
    for item in raw:
        revisions.append(docx_reviser.Revision(
            id=item.get("id", ""),
            type=item["type"],
            anchor_bookmark=item["anchor_bookmark"],
            content=item.get("content", ""),
            style=item.get("style", "body"),
            reason=item.get("reason", ""),
            anchor_position=item.get("anchor_position", "end"),
        ))

    # Determine output path — next טיוטה-v{N}.docx
    export_dir = config.find_case_dir(case_number) / "exports"
    export_dir.mkdir(parents=True, exist_ok=True)
    existing = list(export_dir.glob("טיוטה-v*.docx"))
    next_ver = 1
    for p in existing:
        try:
            ver = int(p.stem.split("-v")[1])
            next_ver = max(next_ver, ver + 1)
        except (IndexError, ValueError):
            pass
    output_path = export_dir / f"טיוטה-v{next_ver}.docx"

    try:
        result = docx_reviser.apply_tracked_revisions(
            active_path, output_path, revisions, author=author,
        )
        await db.set_active_draft_path(case_id, str(output_path))
        await db.mark_blocks_stale(case_id, True)
        case_dir = config.find_case_dir(case_number)
        if case_dir.exists():
            git_sync.commit_and_push(
                case_dir,
                f"revise: טיוטה-v{next_ver} ({result.applied} שינויים, {result.failed} נכשלו)",
            )
        return json.dumps({
            "status": "completed",
            "output_path": str(output_path),
            "version": next_ver,
            "applied": result.applied,
            "failed": result.failed,
            "active_draft_path": str(output_path),
            "results": [
                {"id": r.id, "status": r.status, "error": r.error}
                for r in result.results
            ],
        }, ensure_ascii=False, indent=2)
    except Exception as e:
        return json.dumps({"status": "error", "message": str(e)},
                          ensure_ascii=False, indent=2)


async def get_block_context(case_number: str, block_id: str, instructions: str = "") -> str:
    """קבלת הקשר מלא לכתיבת בלוק — ללא קריאה ל-API. Claude Code כותב את הבלוק.

    Args:
        case_number: מספר תיק הערר
        block_id: מזהה הבלוק (block-he, block-vav, ..., block-yod-bet)
        instructions: הנחיות נוספות
    """
    from legal_mcp.services import block_writer

    case = await db.get_case_by_number(case_number)
    if not case:
        return f"תיק {case_number} לא נמצא."

    case_id = UUID(case["id"])
    try:
        ctx = await block_writer.get_block_context(case_id, block_id, instructions)
        return json.dumps(ctx, default=str, ensure_ascii=False, indent=2)
    except ValueError as e:
        return str(e)


async def save_block_content(case_number: str, block_id: str, content: str) -> str:
    """שמירת בלוק שנכתב ע"י Claude Code ב-DB.

    Args:
        case_number: מספר תיק הערר
        block_id: מזהה הבלוק
        content: הטקסט שנכתב
    """
    from legal_mcp.services import block_writer

    case = await db.get_case_by_number(case_number)
    if not case:
        return f"תיק {case_number} לא נמצא."

    case_id = UUID(case["id"])
    try:
        result = await block_writer.save_block_content(case_id, block_id, content)
        return json.dumps(result, default=str, ensure_ascii=False, indent=2)
    except ValueError as e:
        return str(e)


async def analyze_style(appeal_subtype: str = "") -> str:
    """הרצת ניתוח סגנון על קורפוס ההחלטות של דפנה. מחלץ דפוסי כתיבה ושומר אותם.

    Args:
        appeal_subtype: סינון לפי סוג ערר (building_permit / betterment_levy / compensation_197).
                        ריק = כל ההחלטות.
    """
    from legal_mcp.services.style_analyzer import analyze_corpus

    result = await analyze_corpus(appeal_subtype)
    return json.dumps(result, ensure_ascii=False, indent=2)


async def write_block(
    case_number: str,
    block_id: str,
    instructions: str = "",
) -> str:
    """כתיבת בלוק יחיד בהחלטה. כותב ושומר ב-DB.

    Args:
        case_number: מספר תיק הערר
        block_id: מזהה הבלוק: block-alef, block-bet, block-gimel, block-dalet, block-he, block-vav, block-zayin, block-chet, block-tet, block-yod, block-yod-alef, block-yod-bet
        instructions: הנחיות נוספות לניסוח
    """
    from legal_mcp.services import block_writer

    case = await db.get_case_by_number(case_number)
    if not case:
        return f"תיק {case_number} לא נמצא."

    case_id = UUID(case["id"])

    try:
        result = await block_writer.write_and_store_block(case_id, block_id, instructions)
        return json.dumps(result, default=str, ensure_ascii=False, indent=2)
    except ValueError as e:
        return str(e)


async def write_all_blocks(
    case_number: str,
    start_from: str = "block-alef",
    instructions: str = "",
) -> str:
    """כתיבת כל הבלוקים בהחלטה, בלוק אחרי בלוק. שומר כל בלוק מיד אחרי כתיבה.

    Args:
        case_number: מספר תיק הערר
        start_from: מאיזה בלוק להתחיל (ברירת מחדל: block-alef)
        instructions: הנחיות כלליות
    """
    from legal_mcp.services import block_writer
    from legal_mcp.services.block_writer import BLOCK_CONFIG

    case = await db.get_case_by_number(case_number)
    if not case:
        return f"תיק {case_number} לא נמצא."

    case_id = UUID(case["id"])

    # Determine start index
    start_idx = BLOCK_CONFIG.get(start_from, {}).get("index", 1)

    results = []
    block_order = sorted(BLOCK_CONFIG.items(), key=lambda x: x[1]["index"])

    for bid, cfg in block_order:
        if cfg["index"] < start_idx:
            continue

        try:
            result = await block_writer.write_and_store_block(case_id, bid, instructions)
            results.append({
                "block_id": bid,
                "title": result["title"],
                "word_count": result["word_count"],
                "status": "completed",
            })
        except ValueError as e:
            results.append({
                "block_id": bid,
                "title": cfg["title"],
                "status": "error",
                "error": str(e),
            })
            # Stop on critical error (e.g., missing direction for block-yod)
            if "כיוון מאושר" in str(e):
                break

    total_words = sum(r.get("word_count", 0) for r in results)
    return json.dumps({
        "blocks": results,
        "total_words": total_words,
        "completed": sum(1 for r in results if r["status"] == "completed"),
    }, default=str, ensure_ascii=False, indent=2)
