#
#   Imandra Inc.
#
#   speclogician/presentation/renderers/predicate.py
#

from __future__ import annotations

from rich import box
from rich.console import Group, RenderableType
from rich.panel import Panel
from rich.syntax import Syntax
from rich.table import Table
from rich.text import Text

from speclogician.presentation.ctx import RenderCtx
from speclogician.presentation.models.predicate import PredicatePM, TransitionPM
from speclogician.utils import IMLValidity


# -----------------------------------------------------------------------------
# small helpers
# -----------------------------------------------------------------------------

def _meta_table(*, ctx: RenderCtx) -> Table:
    b = box.MINIMAL if getattr(ctx, "compact", False) else box.SIMPLE
    t = Table(
        show_header=False,
        box=b,
        show_edge=False,
        pad_edge=False,
        expand=False,
    )
    # Make keys a bit more prominent than before (consistent with your newer style)
    t.add_column(style="bold", no_wrap=True)
    t.add_column(justify="left")
    return t


def _badge(v: IMLValidity) -> Text:
    # Normalize: v may be enum; keep it robust
    s = (getattr(v, "value", str(v)) or "").lower()

    if s == "valid":
        return Text("✓ valid", style="bold green")
    if s == "invalid":
        return Text("✗ invalid", style="bold red")
    if s == "unknown":
        return Text("? unknown", style="bold yellow")
    return Text(str(getattr(v, "value", v)), style="bold yellow")


def _empty(text: str = "∅ empty") -> Text:
    return Text(text, style="dim")


def _code_block(*, title: str, code: str, lang: str) -> RenderableType:
    # word_wrap=True tends to look better in narrow terminals
    return Panel(
        Syntax(code, lang, line_numbers=False, word_wrap=True),
        title=title,
        border_style="cyan",
    )


def _snippet(s: str, *, limit: int = 200) -> str:
    s2 = (s or "").strip()
    if not s2:
        return ""
    if len(s2) <= limit:
        return s2
    return s2[:limit] + "…"


def section(t: Table, label: str) -> None:
    t.add_row(Text(label, style="italic dim"), Text(""))

def field(t: Table, name: str, value: Text | str) -> None:
    t.add_row(
        Text(f"  {name}", style="bold"),  # ⬅️ indent
        value if isinstance(value, Text) else Text(str(value)),
    )


# -----------------------------------------------------------------------------
# main renderers
# -----------------------------------------------------------------------------

def render_predicate(pm: PredicatePM, *, ctx: RenderCtx) -> RenderableType:
    t = _meta_table(ctx=ctx)

    # ── Identity ─────────────────────────────
    section(t, "Identity")
    field(t, "name", Text(pm.name or "-", style="bold"))
    field(t, "signature", Text(pm.signature or "", style="dim"))
    field(t, "type", Text(str(pm.pred_type), style="cyan"))
    if ctx.show_ids:
        field(t, "comp_id", Text(pm.comp_id or "", style="dim"))
    field(t, "updated", Text(pm.last_updated.isoformat(), style="dim"))

    # ── Logic ────────────────────────────────
    section(t, "Logic")
    field(t, "iml_valid", _badge(pm.is_iml_valid))
    if pm.iml_error:
        field(t, "iml_error", Text(pm.iml_error, style="red"))

    # ── Connections ──────────────────────────
    section(t, "Connections")
    field(t, "linked artifacts", pm.links.total)
    field(t, "test / log traces", f"{pm.links.test_traces}/{pm.links.log_traces}")
    field(t, "doc / src refs", f"{pm.links.doc_refs}/{pm.links.src_code_refs}")
    if pm.links.unresolved_ids:
        field(t, "unresolved", Text(str(pm.links.unresolved_ids), style="yellow"))

    blocks: list[RenderableType] = [t]

    if ctx.show_code:
        blocks.append(Text(""))
        blocks.append(
            Panel(
                Syntax(pm.iml or "(* empty *)", "ocaml", word_wrap=True),
                title="iml",
                border_style="cyan",
            )
        )

    border = "blue" if pm.kind == "state_predicate" else "magenta"
    return Panel(
        Group(*blocks),
        title=f"[bold]Predicate[/bold]: {pm.name or '-'}",
        border_style=border,
    )


def render_transition(pm: TransitionPM, *, ctx: RenderCtx) -> RenderableType:
    t = _meta_table(ctx=ctx)

    # Section: identity
    t.add_row(Text("Identity", style="italic dim"), Text(""))
    t.add_row(Text("name"), Text(pm.name or "-", style="bold"))
    t.add_row(Text("signature"), Text(pm.signature or "", style="dim"))
    if ctx.show_ids:
        t.add_row(Text("comp_id"), Text(pm.comp_id or "", style="dim"))
    t.add_row(Text("updated"), Text(pm.last_updated.isoformat(), style="dim"))

    # Section: logic
    t.add_row(Text("Logic", style="italic dim"), Text(""))
    t.add_row(Text("iml_valid"), _badge(pm.is_iml_valid))
    if pm.iml_error:
        t.add_row(Text("iml_error"), Text(pm.iml_error, style="red"))

    # Section: connections
    t.add_row(Text("Connections", style="italic dim"), Text(""))
    t.add_row(Text("linked artifacts"), Text(str(pm.links.total)))
    t.add_row(Text("test/log traces"), Text(f"{pm.links.test_traces}/{pm.links.log_traces}"))
    t.add_row(Text("doc/src refs"), Text(f"{pm.links.doc_refs}/{pm.links.src_code_refs}"))
    if pm.links.unresolved_ids:
        t.add_row(Text("unresolved"), Text(str(pm.links.unresolved_ids), style="yellow"))

    blocks: list[RenderableType] = [t]

    if ctx.show_code:
        blocks.append(Text(""))
        blocks.append(
            _code_block(
                title="iml",
                code=(pm.iml or "(* empty *)"),
                lang="ocaml",
            )
        )
    else:
        snip = _snippet(pm.iml or pm.src_code, limit=120)
        blocks.append(Text(""))
        blocks.append(Text(f"iml: {snip}" if snip else "iml: ∅", style="dim"))

    return Panel(
        Group(*blocks),
        title=f"[bold]Transition[/bold]: {pm.name or '-'}",
        border_style="green",
    )