"""Condition 5: TWOSTEP v2 — Two-step pipeline: Discover → Synthesize.

Step 1: Run full dusun pipeline to extract structured findings with
        semantic translation (numeric scores + translated patterns).
Step 2: Feed structured InsightBrief into synthesis prompt.

v2 key change: Preserves structured data (scores, KV pairs) from
SCAFFOLD_FULL while translating framework jargon to plain English.
v1 failed because prose conversion lost structural signal and LLM
ignored opaque framework terms (3.7% utilization rate).
"""

from __future__ import annotations

import re
import json
from typing import Any, Dict, List

from fw_server.server import dusun_tool
from fw_server.session import reset_ledger

from .base import ConditionOutput, ConditionRunner

# ── Lens name mapping (Turkish → English) ─────────────────────────

LENS_NAME_MAP = {
    "Ontoloji": "Ontological",
    "Mereoloji": "Mereological",
    "FOL": "Formal Logic",
    "Bayes": "Bayesian",
    "OyunTeorisi": "Game-Theoretic",
    "KategoriTeorisi": "Category-Theoretic",
    "Topoloji + Holografik": "Topological/Holographic",
}

# ── Path description mapping ──────────────────────────────────────

PATH_DESCRIPTIONS: Dict[str, str] = {
    "P1": "foundational principles and universal concepts",
    "P2": "inferential reasoning and evidence-based argumentation",
    "P3": "applied concepts and practical implications",
    "P4": "integrative synthesis across multiple frameworks",
    "unknown": "diverse analytical perspectives",
}

# ── Pattern translation dictionary ────────────────────────────────

PATTERN_TRANSLATIONS: Dict[str, str] = {
    "B01-B22 dimensions": "Core themes manifest across multiple analytical dimensions",
    "seed omnipresence": "Fundamental ideas appear compressed within each part",
    "Structural reality = constitutive patterns": "Focus on what constitutes entities rather than surface labels",
    "fractal self-similarity": "The same patterns repeat at different scales of analysis",
    "self-similarity": "The same patterns repeat at different scales of analysis",
    "irreducible blind spots": "This text's formal arguments may have assumptions that resist formalization",
    "Zero in ANY dimension collapses": "Removing any single element may undermine the whole argument",
    "Names are realia not nominalia": "Names/terms in this text reflect real properties, not arbitrary labels",
    "harfî mode": "Individual components carry intrinsic meaning beyond their surface form",
    "harfî": "Individual components carry intrinsic meaning beyond their surface form",
    "Defects are privations": "Flaws or defects function as absences rather than positive properties",
    "kavaid battery": "Core analytical principles can be systematically applied to test each claim",
    "Parts contain compressed images of wholes": "Each part contains a compressed version of the whole argument",
    "compressed images": "Each part contains a compressed version of the whole argument",
}


def _translate_pattern(raw_text: str) -> str:
    """Translate framework pattern text to an English analytical directive."""
    for key, translation in PATTERN_TRANSLATIONS.items():
        if key.lower() in raw_text.lower():
            return translation
    # No known pattern matches — strip framework refs and return cleaned text
    cleaned = re.sub(r"\b[A-Z]{2,}\d+[-–]\d+\b", "", raw_text)
    cleaned = re.sub(r"\b[PBTKAX]\d+\b", "", cleaned)
    return cleaned.strip() or raw_text


# ── Guidance translation dictionary ───────────────────────────────

GUIDANCE_TRANSLATIONS: Dict[str, str] = {
    "ontological stack": "Trace concepts from observable effects to underlying causes",
    "station calibration": "Evaluate the text's level of analytical rigor",
    "formalization = rational inference": "Assess how well arguments can be formally expressed",
    "Apply detected patterns": "Use the detected patterns above to guide your analysis",
    "Lead with structural insight": "Begin by identifying deep structure before surface details",
    "Map concepts through": "Trace each concept from its surface effects to its underlying cause",
}


def _translate_guidance(raw_guidance: str) -> str:
    """Translate framework guidance to plain English directives."""
    # Remove framework references like "(T15)", "§8.2", "AX2-4"
    cleaned = re.sub(r"\s*\([^)]*(?:AX|KV|T\d|§)[^)]*\)", "", raw_guidance)
    cleaned = re.sub(r"\s*(?:AX\d+[-–]\d+|§\d+\.\d+)", "", cleaned)
    cleaned = re.sub(r"\s+unless P\d+\.?", ".", cleaned)
    # Apply semantic translations
    for key, translation in GUIDANCE_TRANSLATIONS.items():
        if key.lower() in cleaned.lower():
            cleaned = translation
            break
    return cleaned.strip()


# ── Max InsightBrief length (words ≈ tokens × 0.75) ──────────────

_MAX_BRIEF_WORDS = 500  # raised from 400 for structured format


# ── Insight extraction (v2: structured + translated) ──────────────

def extract_insight_brief(result: dict) -> str:
    """Convert dusun structured result into a structured InsightBrief.

    v2: Preserves numeric scores and KV structure (SCAFFOLD_FULL's
    discovery advantage) while translating jargon to English.
    """
    sections: List[str] = []

    # 1. Domain context (translated)
    path = result.get("path", "unknown") or "unknown"
    path_desc = PATH_DESCRIPTIONS.get(path, PATH_DESCRIPTIONS["unknown"])
    sections.append(f"Domain: {path_desc} (path-{path[-1] if path.startswith('P') else '?'})")

    # 2. ALL lens scores (translated names, numeric values preserved)
    lens_scores = result.get("lens_scores", {})
    if lens_scores:
        sorted_lenses = sorted(lens_scores.items(), key=lambda x: x[1], reverse=True)
        score_parts = []
        for lens_tr, score in sorted_lenses:
            eng = LENS_NAME_MAP.get(lens_tr, lens_tr)
            score_parts.append(f"{eng}: {score:.2f}")
        sections.append("Lens relevance scores: " + ", ".join(score_parts))

    # 3. Pattern discoveries (translated via dictionary)
    sd = result.get("shaping_directives") or {}
    pattern_dirs: list = sd.get("pattern_directives", [])
    translated_patterns: List[str] = []
    seen_translations: set = set()
    for pd in pattern_dirs:
        apply_text = pd.get("apply", "")
        if not apply_text:
            continue
        translated = _translate_pattern(apply_text)
        # Deduplicate by translation
        key = translated.lower()[:60]
        if key not in seen_translations:
            seen_translations.add(key)
            translated_patterns.append(translated)
    # Also check translation_hints for additional patterns (deduplicated)
    hints = result.get("translation_hints", {})
    for _key, hint_text in hints.items():
        if not hint_text:
            continue
        translated = _translate_pattern(hint_text)
        key = translated.lower()[:60]
        if key not in seen_translations:
            seen_translations.add(key)
            translated_patterns.append(translated)
    if translated_patterns:
        pattern_block = "\n".join(f"  - {p}" for p in translated_patterns[:6])
        sections.append(f"Detected patterns and their significance:\n{pattern_block}")

    # 4. Anomalies (filter diagnostics, keep substantive)
    anomalies = result.get("anomalies", [])
    substantive_anomalies: List[str] = []
    for a in anomalies[:3]:
        if any(skip in a for skip in (
            "KAVAID-FAIL", "REMAINING-ZERO", "returned 0.0", "check if"
        )):
            continue
        cleaned = a
        for prefix in ("ZERO-SCORE: ", "CONVERGENCE: "):
            if cleaned.startswith(prefix):
                cleaned = cleaned[len(prefix):]
        substantive_anomalies.append(cleaned)
    if substantive_anomalies:
        sections.append("Notable observations: " + "; ".join(substantive_anomalies))

    # 5. Focus guidance (translated + structured directive)
    guidance_parts: List[str] = []
    # Always add the score-based focus directive
    if lens_scores:
        high_lenses = [
            LENS_NAME_MAP.get(k, k)
            for k, v in sorted(lens_scores.items(), key=lambda x: x[1], reverse=True)
            if v > 0.5
        ]
        if high_lenses:
            guidance_parts.append(
                f"Pay special attention to lenses with scores > 0.5: {', '.join(high_lenses)}"
            )
    # Path-specific guidance (translated)
    path_guidance = sd.get("path_guidance", "")
    if path_guidance:
        translated_g = _translate_guidance(path_guidance)
        if translated_g:
            guidance_parts.append(translated_g)
    if guidance_parts:
        sections.append("Focus guidance: " + ". ".join(guidance_parts) + ".")

    # ── Assemble structured brief ─────────────────────────────────
    brief = "=== PRELIMINARY ANALYSIS FINDINGS ===\n" + "\n".join(sections) + "\n=== END FINDINGS ==="

    # Truncate if over word limit
    words = brief.split()
    if len(words) > _MAX_BRIEF_WORDS:
        brief = " ".join(words[:_MAX_BRIEF_WORDS]) + "\n=== END FINDINGS ==="

    return brief


# ── Synthesis prompt (based on VOCAB_ONLY template) ───────────────

TWOSTEP_SYNTHESIS_PROMPT = """\
Analyze the following text through these 7 analytical perspectives.
For each perspective, provide substantive analysis (not just mention).

1. **Ontological Analysis**: Identify the core entities, their categories,
   hierarchical relations, and constitutive dependencies.

2. **Mereological Analysis**: Identify whole-part structures, teleological
   relations between parts, and how parts relate to the whole.

3. **Formal Logic (FOL)**: Identify premises, conclusions, logical structure.
   Formalize key arguments. Check for validity and soundness.

4. **Bayesian Analysis**: Identify hypotheses, evidence, prior probabilities.
   Show how evidence updates beliefs. Quantify where possible.

5. **Game-Theoretic Analysis**: Identify agents/players, strategies, payoffs,
   equilibria. What incentive structures are at play?

6. **Category-Theoretic Analysis**: Identify objects, morphisms, functors.
   What structural transformations preserve essential properties?

7. **Topological/Holographic Analysis**: Identify dimensional structure,
   continuity properties, seed-whole relationships, fractal patterns.

A structured preliminary analysis has been performed on this text. The
findings below include numerical relevance scores for each lens and
translated pattern detections. Use these findings to focus your analysis:
- Prioritize lenses with higher relevance scores.
- Investigate detected patterns concretely within the text.
- Verify or refine each finding based on your own reading.

{insight_brief}

After analyzing each lens, synthesize: How do the lenses connect?
What does the combination reveal that no single lens shows alone?

Text:
{text}
"""


# ── Condition Runner ──────────────────────────────────────────────

class TwoStepRunner(ConditionRunner):
    """Condition 5 — two-step pipeline: dusun discovery → clean synthesis."""

    def __init__(self, llm_client):
        self.llm_client = llm_client

    def condition_id(self) -> str:
        return "TWOSTEP"

    async def run(self, text: str, text_id: str) -> ConditionOutput:
        # Step 1: Run dusun for insight extraction
        reset_ledger()
        raw = await dusun_tool(
            text=text,
            ctx=None,
            context=None,
            mode="analyze",
        )
        result: Dict[str, Any] = json.loads(raw)
        insight_brief = extract_insight_brief(result)

        # Step 2: VOCAB-style synthesis with injected insights
        prompt = TWOSTEP_SYNTHESIS_PROMPT.format(
            insight_brief=insight_brief,
            text=text,
        )
        narrative = await self.llm_client.generate(prompt)

        return ConditionOutput(
            condition_id="TWOSTEP",
            text_id=text_id,
            raw_output=narrative,
            metadata={
                "insight_brief": insight_brief,
                "step1_path": result.get("path"),
                "step1_fire_plan": result.get("fire_plan"),
                "step1_lens_scores": result.get("lens_scores"),
                "step1_patterns": result.get("patterns_applied"),
                "prompt_template": "TWOSTEP_SYNTHESIS_PROMPT",
            },
        )
