#
#   Imandra Inc.
#
#   speclogician/presentation/builders/state_diff.py
#

from __future__ import annotations

from typing import Any

from speclogician.presentation.ctx import RenderCtx
from speclogician.presentation.models.state_diff import (
    StateDiffPM,
    DiffSectionPM,
    DiffRowPM,
)
from speclogician.state.diff import StateDiff, ValueDiff, ComparisonOutcome


def _outcome(vd: ValueDiff[Any]) -> ComparisonOutcome:
    if vd.comp_func:
        return vd.comp_func(vd.before, vd.after)
    if vd.before == vd.after:
        return ComparisonOutcome.NO_CHANGE
    return ComparisonOutcome.UNKNOWN


def _message(vd: ValueDiff[Any]) -> str:
    oc = _outcome(vd)
    b = vd.before
    a = vd.after

    match oc:
        case ComparisonOutcome.UNKNOWN:
            return f"{vd.label}: Unknown: {b} -> {a}"
        case ComparisonOutcome.NO_CHANGE:
            return f"{vd.label}: No change: {b} -> {a}"
        case ComparisonOutcome.NO_CHANGE_GOOD:
            return f"{vd.label}: No change (good): {b} -> {a}"
        case ComparisonOutcome.NO_CHANGE_BAD:
            return f"{vd.label}: No change (bad): {b} -> {a}"
        case ComparisonOutcome.IMPROVED:
            return f"{vd.label}: Improvement: {b} -> {a}"
        case ComparisonOutcome.DECLINED:
            return f"{vd.label}: Decline: {b} -> {a}"
        case _:
            return f"{vd.label}: {b} -> {a}"


def _row(vd: ValueDiff[Any]) -> DiffRowPM:
    return DiffRowPM(
        label=vd.label,
        before=vd.before,
        after=vd.after,
        outcome=_outcome(vd),
        message=_message(vd),
    )


def build_state_diff_pm(sd: StateDiff, ctx: RenderCtx) -> StateDiffPM:
    sections: list[DiffSectionPM] = []

    def add(title: str, vds: list[ValueDiff[Any]]) -> None:
        rows = [_row(vd) for vd in vds if vd.is_different()]
        if rows:
            sections.append(DiffSectionPM(title=title, rows=rows))

    # ---- Domain model ----
    add("Domain Model: Base", [sd.base_status, sd.base_has_state, sd.base_has_action])

    add(
        "Domain Model: State predicates",
        [
            sd.num_state_preds_total,
            sd.num_state_preds_matched,
            sd.num_state_preds_valid_logic,
            sd.num_state_preds_errored,
        ],
    )

    add(
        "Domain Model: Action predicates",
        [
            sd.num_action_preds_total,
            sd.num_action_preds_matched,
            sd.num_action_preds_valid_logic,
            sd.num_action_preds_errored,
        ],
    )

    add(
        "Domain Model: Predicates (total)",
        [
            sd.num_preds_total,
            sd.num_preds_matched,
            sd.num_preds_valid_logic,
            sd.num_preds_errored,
        ],
    )

    add(
        "Domain Model: Transitions",
        [
            sd.num_trans_total,
            sd.num_trans_matched,
            sd.num_trans_valid_logic,
            sd.num_trans_errored,
        ],
    )

    # ---- Scenarios + conflicts ----
    add(
        "Scenarios",
        [
            sd.num_sc_total,
            sd.num_sc_missing,
            sd.num_sc_matched,
            sd.num_sc_inconsistent,
        ],
    )

    add(
        "Scenario conflicts",
        [
            sd.num_sc_conflicted,
            sd.num_sc_overlap,
            sd.num_sc_consumed,
        ],
    )

    # ---- Artifacts ----
    add(
        "Test traces",
        [
            sd.num_test_traces_total,
            sd.num_test_traces_logic_good,
            sd.num_test_traces_matched,
        ],
    )

    add(
        "Log traces",
        [
            sd.num_log_traces_total,
            sd.num_log_traces_logic_good,
            sd.num_log_traces_matched,
        ],
    )

    return StateDiffPM(has_changes=bool(sections), sections=sections)