# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Licensed under the MIT License - see LICENSE file for details

"""Tests for Pipeline 2: PlanTracker runtime progress tracking."""

from familiar.core.planner import PlanStep, PlanTracker, TaskPlan


def _make_plan(steps=None):
    """Helper to build a simple TaskPlan."""
    if steps is None:
        steps = [
            PlanStep(id=1, description="Search the web", tool="web_search"),
            PlanStep(id=2, description="Summarize results", tool=None),
            PlanStep(id=3, description="Remember key facts", tool="remember"),
        ]
    return TaskPlan(goal="Test plan", steps=steps)


# ── Step marked done on matching tool call ────────────────────

def test_record_tool_call_marks_matching_step():
    plan = _make_plan()
    tracker = PlanTracker(plan)
    tracker.record_tool_call("web_search", success=True)
    assert plan.steps[0].status == "done"
    assert 1 in tracker._done


def test_record_tool_call_skips_already_done():
    plan = _make_plan()
    tracker = PlanTracker(plan)
    tracker.record_tool_call("web_search", success=True)
    # Second call to same tool should mark a different step (or no-op)
    tracker.record_tool_call("web_search", success=True)
    # Step 1 already done; no other step has tool=web_search
    # Falls through to non-tool step (step 2)
    assert 2 in tracker._done


# ── Failed call doesn't mark step ─────────────────────────────

def test_failed_call_does_not_mark():
    plan = _make_plan()
    tracker = PlanTracker(plan)
    tracker.record_tool_call("web_search", success=False)
    assert 1 not in tracker._done
    assert plan.steps[0].status == "pending"


# ── Progress prompt shows done/remaining ──────────────────────

def test_progress_prompt_format():
    plan = _make_plan()
    tracker = PlanTracker(plan)
    tracker.record_tool_call("web_search", success=True)

    prompt = tracker.get_progress_prompt()
    assert "<plan_progress>" in prompt
    assert "</plan_progress>" in prompt
    assert "1/3 steps complete" in prompt
    # Step 1 should show ✓, others ○
    assert "✓" in prompt
    assert "○" in prompt


# ── is_complete works ─────────────────────────────────────────

def test_is_complete_false_initially():
    tracker = PlanTracker(_make_plan())
    assert not tracker.is_complete


def test_is_complete_true_when_all_done():
    plan = _make_plan()
    tracker = PlanTracker(plan)
    tracker.record_tool_call("web_search", success=True)  # step 1
    tracker.record_tool_call("web_search", success=True)  # step 2 (no-tool fallback)
    tracker.record_tool_call("remember", success=True)    # step 3
    assert tracker.is_complete


# ── get_current_step ──────────────────────────────────────────

def test_get_current_step():
    plan = _make_plan()
    tracker = PlanTracker(plan)
    step = tracker.get_current_step()
    assert step.id == 1

    tracker.record_tool_call("web_search", success=True)
    step = tracker.get_current_step()
    assert step.id == 2


def test_get_current_step_none_when_complete():
    plan = _make_plan([PlanStep(id=1, description="Do thing", tool="tool_a")])
    tracker = PlanTracker(plan)
    tracker.record_tool_call("tool_a", success=True)
    assert tracker.get_current_step() is None


# ── Error feedback includes plan step context ─────────────────

def test_error_feedback_with_plan_step():
    from familiar.core.self_correction import ToolRetryTracker

    tracker = ToolRetryTracker()
    tracker.record_failure("web_search", "Connection refused")
    result = tracker.format_error_feedback(
        "web_search", "Connection refused", plan_step="Search the web for results"
    )
    assert "Failing plan step:" in result
    assert "Search the web for results" in result
    assert "Consider a different approach" in result


def test_error_feedback_without_plan_step():
    from familiar.core.self_correction import ToolRetryTracker

    tracker = ToolRetryTracker()
    tracker.record_failure("web_search", "Connection refused")
    result = tracker.format_error_feedback("web_search", "Connection refused")
    assert "Failing plan step:" not in result
