#
#   Imandra Inc.
#
#   speclogician/presentation/preview/fixtures/domain_model.py
#

from __future__ import annotations

from speclogician.modeling.domain import BaseStatus
from speclogician.presentation.models.domain import (
    DomainCountsPM,
    DomainCodePM,
    DomainModelPM,
)
from speclogician.presentation.ctx import RenderCtx


# -----------------------------------------------------------------------------
# PM builders (variants)
# -----------------------------------------------------------------------------

def make_domain_model_pm(ctx: RenderCtx, variant: str = "typical") -> DomainModelPM:
    v = (variant or "typical").lower()
    if v == "minimal":
        return minimal_domain_model_pm()
    if v == "edge":
        return edge_domain_model_pm()
    return typical_domain_model_pm()


def minimal_domain_model_pm() -> DomainModelPM:
    # Minimal = almost empty, base unknown, no code.
    counts = DomainCountsPM(
        base_status=BaseStatus.UNKNOWN,
        base_has_state=False,
        base_has_action=False,
        num_state_preds_total=0,
        num_state_preds_valid_logic=0,
        num_state_preds_matched=0,
        num_action_preds_total=0,
        num_action_preds_valid_logic=0,
        num_action_preds_matched=0,
        num_preds_total=0,
        num_preds_valid_logic=0,
        num_preds_matched=0,
        num_trans_total=0,
        num_trans_valid_logic=0,
        num_trans_matched=0,
    )
    code = DomainCodePM(
        base="",
        state_preds_iml="",
        action_preds_iml="",
        transitions_iml="",
    )
    return DomainModelPM(counts=counts, code=code)


def typical_domain_model_pm() -> DomainModelPM:
    # Typical = sane + readable, some matched/valid numbers.
    counts = DomainCountsPM(
        base_status=BaseStatus.VALID,
        base_has_state=True,
        base_has_action=True,
        # state preds
        num_state_preds_total=4,
        num_state_preds_valid_logic=3,
        num_state_preds_matched=2,
        # action preds
        num_action_preds_total=3,
        num_action_preds_valid_logic=3,
        num_action_preds_matched=1,
        # totals
        num_preds_total=7,
        num_preds_valid_logic=6,
        num_preds_matched=3,
        # transitions
        num_trans_total=2,
        num_trans_valid_logic=2,
        num_trans_matched=1,
    )
    code = DomainCodePM(
        base=(
            "type state = { x : int }\n"
            "type action = int\n"
        ),
        state_preds_iml=(
            "let pred_eq_ten (s: state) = s.x = 10\n"
            "let pred_lt_ten (s: state) = s.x < 10\n"
            "let pred_gt_zero (s: state) = s.x > 0\n"
        ),
        action_preds_iml=(
            "let act_gt_x (s: state) (a: action) = a > s.x\n"
            "let act_eq_zero (_s: state) (a: action) = a = 0\n"
        ),
        transitions_iml=(
            "let step1 (s: state) (a: action) : state = { x = s.x + a }\n"
            "let step2 (_s: state) (a: action) : state = { x = a }\n"
        ),
    )
    return DomainModelPM(counts=counts, code=code)


def edge_domain_model_pm() -> DomainModelPM:
    # Edge = stress the renderer: long code, weird counts, invalid base.
    counts = DomainCountsPM(
        base_status=BaseStatus.INVALID_IML,
        base_has_state=True,
        base_has_action=False,
        # state preds
        num_state_preds_total=12,
        num_state_preds_valid_logic=7,
        num_state_preds_matched=0,
        # action preds
        num_action_preds_total=0,
        num_action_preds_valid_logic=0,
        num_action_preds_matched=0,
        # totals
        num_preds_total=12,
        num_preds_valid_logic=7,
        num_preds_matched=0,
        # transitions
        num_trans_total=5,
        num_trans_valid_logic=2,
        num_trans_matched=0,
    )
    code = DomainCodePM(
        base=(
            "type state = {\n"
            "  x : int;\n"
            "  (* missing action type on purpose to simulate INVALID base *)\n"
            "}\n"
        ),
        state_preds_iml=(
            "(* many predicates, including a long one to wrap *)\n"
            + "\n".join(
                f"let pred_{i} (s: state) = s.x = {i}"
                for i in range(1, 10)
            )
            + "\n"
            "let pred_very_long (s: state) = "
            "(s.x > 0 && s.x < 1000000 && (s.x mod 3 = 0 || s.x mod 5 = 0))\n"
        ),
        action_preds_iml="",  # missing because base_has_action=False
        transitions_iml=(
            "(* some transitions, one intentionally sketchy *)\n"
            "let step_ok (s: state) (_a: int) : state = { x = s.x }\n"
            "let step_weird (s: state) (a: int) : state = { x = s.x + (a + 1) }\n"
            "(* malformed-ish snippet *)\n"
            "let step_bad (s: state) (a: int) : state = { x = s.x + }\n"
        ),
    )
    return DomainModelPM(counts=counts, code=code)


# -----------------------------------------------------------------------------
# renderer adapter (matches other fixtures style)
# -----------------------------------------------------------------------------

def render(pm: DomainModelPM, ctx: RenderCtx):
    from speclogician.presentation.renderers.domain import render_domain_model
    return render_domain_model(pm, ctx=ctx)