"""Tests for Plan-and-Solve framework."""

import pytest
from plansolve import (
    PlanAndSolveAgent,
    Planner,
    Solver,
    SelfChecker,
    Plan,
    PlanStep,
    Solution,
    SolveStep,
    CheckResult,
    StepStatus,
)


class TestPlanStep:
    """Test PlanStep dataclass."""

    def test_step_creation(self):
        step = PlanStep(description="Test step", step_number=1)
        assert step.description == "Test step"
        assert step.step_number == 1
        assert step.status == StepStatus.PENDING

    def test_step_to_dict(self):
        step = PlanStep(description="Test", step_number=2, step_id="s1")
        d = step.to_dict()
        assert d["description"] == "Test"
        assert d["step_number"] == 2


class TestPlan:
    """Test Plan class."""

    def test_plan_creation(self):
        plan = Plan(problem="Test problem")
        assert plan.problem == "Test problem"
        assert len(plan.steps) == 0

    def test_add_step(self):
        plan = Plan(problem="Test")
        step = plan.add_step("Step 1")
        assert len(plan.steps) == 1
        assert step.step_number == 1

    def test_get_step(self):
        plan = Plan(problem="Test")
        plan.add_step("Step 1")
        plan.add_step("Step 2")

        step = plan.get_step(2)
        assert step.description == "Step 2"

    def test_get_next_pending(self):
        plan = Plan(problem="Test")
        plan.add_step("Step 1")
        plan.add_step("Step 2")

        next_step = plan.get_next_pending()
        assert next_step.step_number == 1

    def test_is_complete(self):
        plan = Plan(problem="Test")
        step = plan.add_step("Step 1")
        assert not plan.is_complete()
        step.status = StepStatus.COMPLETED
        assert plan.is_complete()

    def test_to_prompt(self):
        plan = Plan(problem="Test problem")
        plan.add_step("Step 1")
        prompt = plan.to_prompt()
        assert "Test problem" in prompt
        assert "Step 1" in prompt


class TestPlanner:
    """Test Planner class."""

    def test_planner_creation(self):
        planner = Planner()
        assert planner is not None

    def test_create_plan(self):
        planner = Planner()
        plan = planner.create_plan("What is 2+2?")
        assert plan.problem == "What is 2+2?"
        assert len(plan.steps) > 0

    def test_custom_plan_fn(self):
        def custom_plan(problem):
            return ["Custom step 1", "Custom step 2"]

        planner = Planner(plan_fn=custom_plan)
        plan = planner.create_plan("Test")
        assert len(plan.steps) == 2
        assert "Custom" in plan.steps[0].description


class TestSolver:
    """Test Solver class."""

    def test_solver_creation(self):
        solver = Solver()
        assert solver is not None

    def test_execute_plan(self):
        plan = Plan(problem="Test")
        plan.add_step("Step 1")
        plan.add_step("Step 2")

        solver = Solver()
        solution = solver.execute_plan(plan)

        assert solution.answer != ""
        assert len(solution.steps) == 2

    def test_custom_solve_fn(self):
        def custom_solve(step, context):
            return f"Executed: {step}"

        plan = Plan(problem="Test")
        plan.add_step("Step 1")

        solver = Solver(solve_fn=custom_solve)
        solution = solver.execute_plan(plan)

        assert "Executed:" in solution.steps[0].execution


class TestSelfChecker:
    """Test SelfChecker class."""

    def test_checker_creation(self):
        checker = SelfChecker()
        assert checker is not None

    def test_check_solution(self):
        plan = Plan(problem="Test problem")
        plan.add_step("Solve")

        solution = Solution(answer="Test answer", reasoning="Some reasoning")

        checker = SelfChecker()
        result = checker.check("Test problem", plan, solution)

        assert isinstance(result, CheckResult)
        assert result.score >= 0

    def test_check_empty_solution(self):
        plan = Plan(problem="Test")
        solution = Solution(answer="", reasoning="")

        checker = SelfChecker()
        result = checker.check("Test", plan, solution)

        assert len(result.issues) > 0

    def test_quick_check(self):
        checker = SelfChecker()
        result = checker.quick_check("The answer is 42", ["answer", "42"])

        assert result.passed is True

    def test_quick_check_missing(self):
        checker = SelfChecker()
        result = checker.quick_check("This is wrong", ["answer", "correct"])

        assert result.passed is False
        assert len(result.issues) > 0


class TestPlanAndSolveAgent:
    """Test PlanAndSolveAgent class."""

    def test_agent_creation(self):
        agent = PlanAndSolveAgent()
        assert agent is not None

    def test_solve(self):
        agent = PlanAndSolveAgent()
        result = agent.solve("What is 2+2?")

        assert result is not None
        assert result.answer != ""
        assert result.plan is not None

    def test_quick_solve(self):
        agent = PlanAndSolveAgent()
        answer = agent.quick_solve("Test problem")

        assert answer != ""

    def test_custom_functions(self):
        def plan_fn(p):
            return ["Step 1", "Step 2"]

        def solve_fn(s, c):
            return "Result"

        agent = PlanAndSolveAgent(plan_fn=plan_fn, solve_fn=solve_fn)
        result = agent.solve("Test")

        assert len(result.plan.steps) == 2


class TestSolution:
    """Test Solution dataclass."""

    def test_solution_creation(self):
        solution = Solution(answer="42", reasoning="Calculated")
        assert solution.answer == "42"
        assert solution.reasoning == "Calculated"

    def test_solution_to_dict(self):
        solution = Solution(answer="Test", solution_id="s1")
        d = solution.to_dict()
        assert d["answer"] == "Test"
        assert d["solution_id"] == "s1"


class TestCheckResult:
    """Test CheckResult dataclass."""

    def test_check_result_passed(self):
        result = CheckResult(passed=True, score=1.0)
        assert result.passed is True
        assert result.issues == []

    def test_check_result_failed(self):
        result = CheckResult(
            passed=False,
            issues=["Issue 1"],
            suggestions=["Fix 1"]
        )
        assert result.passed is False
        assert len(result.issues) == 1