from collections.abc import Callable
from dataclasses import dataclass, field

import z3

type ProblemCallable = Callable[[], z3.BoolRef]


@dataclass(eq=True, frozen=True, order=True)
class Problem:
    name: str
    fun: ProblemCallable = field(compare=False)

    @property
    def formula(self) -> z3.BoolRef:
        formula = (self.fun)()
        assert z3.is_bool(formula), f"Problem {self.name} did not return formula"
        return formula

    def __call__(self) -> z3.BoolRef:
        return self.formula

    def to_smt(self) -> str:
        solver = z3.Solver()
        solver.add(self.formula)
        return solver.sexpr() + "\n(check-sat)"
