from dual_agent.domain import ReviewArtifact, ReviewIssue, TaskState
from tests._fakes import fake_orchestrator, task_in_state


def issue(issue_id, kind):
    return ReviewIssue(issue_id=issue_id, severity="major", problem=issue_id, kind=kind)


def review(*issues):
    return ReviewArtifact(verdict="CHANGES_REQUIRED", issues=list(issues))


def test_review_artifact_separates_blocking_and_advisory_issues():
    artifact = review(
        issue("code", "CODE_BLOCKER"),
        issue("manual", "MANUAL_ACCEPTANCE"),
        issue("hygiene", "DELIVERY_HYGIENE"),
    )

    assert artifact.blocking_issue_ids() == ["code"]
    assert artifact.advisory_issue_ids() == ["hygiene", "manual"]


def test_advisory_only_review_advances_without_fix():
    orchestrator = fake_orchestrator()
    task = task_in_state(orchestrator, "REVIEW")

    result = orchestrator._apply_stage_outcome(
        task, TaskState.REVIEW, review(issue("manual", "MANUAL_ACCEPTANCE"))
    )

    assert result.state is TaskState.FINAL_VERIFY
    assert result.open_issues == []
    assert result.advisory_issues == ["manual"]


def test_mixed_review_enters_fix_with_only_blockers():
    orchestrator = fake_orchestrator()
    task = task_in_state(orchestrator, "REVIEW")

    result = orchestrator._apply_stage_outcome(
        task,
        TaskState.REVIEW,
        review(issue("code", "CODE_BLOCKER"), issue("follow-up", "FOLLOW_UP")),
    )

    assert result.state is TaskState.FIX
    assert result.open_issues == ["code"]
    assert result.advisory_issues == ["follow-up"]


def test_advisory_only_final_verify_completes():
    orchestrator = fake_orchestrator()
    task = task_in_state(orchestrator, "FINAL_VERIFY")

    result = orchestrator._apply_stage_outcome(
        task, TaskState.FINAL_VERIFY, review(issue("device", "MANUAL_ACCEPTANCE"))
    )

    assert result.state is TaskState.DONE
    assert result.advisory_issues == ["device"]


def test_second_plan_rejection_replans_instead_of_stalling():
    orchestrator = fake_orchestrator()
    task = task_in_state(orchestrator, "PLAN_REVIEW")
    task = orchestrator._save(task, plan_rounds=1)

    result = orchestrator._apply_stage_outcome(
        task, TaskState.PLAN_REVIEW, review(issue("still-missing", "CODE_BLOCKER"))
    )

    assert result.state is TaskState.PLAN
    assert result.plan_rounds == 2
    assert result.error is None


def test_plan_review_handoff_preserves_actionable_issue_fields():
    orchestrator = fake_orchestrator()
    task = task_in_state(orchestrator, "PLAN_REVIEW")
    artifact = ReviewArtifact(
        verdict="CHANGES_REQUIRED",
        issues=[ReviewIssue(
            issue_id="missing-guard",
            severity="blocker",
            problem="waiting state falls through",
            file="dual_agent/services.py",
            location="_advance",
            expected_fix="return before provider invocation",
            verification="assert provider call count is zero",
            kind="CODE_BLOCKER",
        )],
    )

    handoff = orchestrator._discussion_handoff(task, artifact)

    assert "missing-guard" in handoff
    assert "dual_agent/services.py" in handoff
    assert "_advance" in handoff
    assert "return before provider invocation" in handoff
    assert "assert provider call count is zero" in handoff

def test_configured_loop_limits_match_operational_policy():
    from dual_agent.domain import Agents, Limits
    from dual_agent.services import MAX_OPERATOR_REPLANS

    assert Agents().discussion_rounds == 6
    assert Agents(discussion_rounds=6).discussion_rounds == 6
    assert Limits().max_fix_cycles == 8
    assert MAX_OPERATOR_REPLANS == 2


def test_third_plan_rejection_stops_for_human_review():
    orchestrator = fake_orchestrator()
    task = task_in_state(orchestrator, "PLAN_REVIEW")
    task = orchestrator._save(task, plan_rounds=2)

    result = orchestrator._apply_stage_outcome(
        task, TaskState.PLAN_REVIEW, review(issue("still-wrong", "CODE_BLOCKER"))
    )

    assert result.state is TaskState.NEEDS_HUMAN
    assert result.plan_rounds == 3


def test_final_verify_handoff_treats_successful_runner_report_as_authoritative():
    orchestrator = fake_orchestrator()
    task = task_in_state(orchestrator, "FINAL_VERIFY")

    handoff = orchestrator._handoff(task, TaskState.FINAL_VERIFY)

    assert "ORCHESTRATOR TEST EVIDENCE IS AUTHORITATIVE" in handoff
    assert "Do not create a TEST_BLOCKER merely because your own sandbox cannot rerun tests" in handoff
