"""Tests for the three-phase loop scaffold.

Validates the acceptance criteria from issue #324:
1. Loop accepts context payload as input to Phase 1
2. Phase 1 output feeds into Phase 2 without manual intervention
3. Phase 2 output feeds into Phase 3 without manual intervention
4. Phase 3 output feeds back into Phase 1
5. Full cycle completes without crash
6. No state leaks between cycles
7. Each phase logs what it received and what it produced
"""

from datetime import datetime

from loop.phase1_gather import gather
from loop.phase2_reason import reason
from loop.phase3_act import act
from loop.runner import run_cycle
from loop.schema import ContextPayload


def _make_payload(source: str = "test", content: str = "hello") -> ContextPayload:
    return ContextPayload(source=source, content=content, token_count=5)


# --- Schema ---


def test_context_payload_defaults():
    p = ContextPayload(source="user", content="hi")
    assert p.source == "user"
    assert p.content == "hi"
    assert p.token_count == -1
    assert p.metadata == {}
    assert isinstance(p.timestamp, datetime)


def test_with_metadata_returns_new_payload():
    p = _make_payload()
    p2 = p.with_metadata(foo="bar")
    assert p2.metadata == {"foo": "bar"}
    assert p.metadata == {}  # original unchanged


def test_with_metadata_merges():
    p = _make_payload().with_metadata(a=1)
    p2 = p.with_metadata(b=2)
    assert p2.metadata == {"a": 1, "b": 2}


# --- Individual phases ---


def test_gather_marks_phase():
    result = gather(_make_payload())
    assert result.metadata["phase"] == "gather"
    assert result.metadata["gathered"] is True


def test_reason_marks_phase():
    gathered = gather(_make_payload())
    result = reason(gathered)
    assert result.metadata["phase"] == "reason"
    assert result.metadata["reasoned"] is True


def test_act_marks_phase():
    gathered = gather(_make_payload())
    reasoned = reason(gathered)
    result = act(reasoned)
    assert result.metadata["phase"] == "act"
    assert result.metadata["acted"] is True


# --- Full cycle ---


def test_full_cycle_completes():
    """Acceptance criterion 5: full cycle completes without crash."""
    payload = _make_payload(source="user", content="What is sovereignty?")
    result = run_cycle(payload)
    assert result.metadata["gathered"] is True
    assert result.metadata["reasoned"] is True
    assert result.metadata["acted"] is True


def test_full_cycle_preserves_source():
    """Source field survives the full pipeline."""
    result = run_cycle(_make_payload(source="timer"))
    assert result.source == "timer"


def test_full_cycle_preserves_content():
    """Content field survives the full pipeline."""
    result = run_cycle(_make_payload(content="test data"))
    assert result.content == "test data"


def test_no_state_leaks_between_cycles():
    """Acceptance criterion 6: no state leaks between cycles."""
    r1 = run_cycle(_make_payload(source="cycle1", content="first"))
    r2 = run_cycle(_make_payload(source="cycle2", content="second"))
    assert r1.source == "cycle1"
    assert r2.source == "cycle2"
    assert r1.content == "first"
    assert r2.content == "second"


def test_cycle_output_feeds_back_as_input():
    """Acceptance criterion 4: Phase 3 output feeds back into Phase 1."""
    first = run_cycle(_make_payload(source="initial"))
    second = run_cycle(first)
    # Second cycle should still work — no crash, metadata accumulates
    assert second.metadata["gathered"] is True
    assert second.metadata["acted"] is True


def test_phases_log(caplog):
    """Acceptance criterion 7: each phase logs what it received and produced."""
    import logging

    with caplog.at_level(logging.INFO):
        run_cycle(_make_payload())

    messages = caplog.text
    assert "Phase 1 (Gather) received" in messages
    assert "Phase 1 (Gather) produced" in messages
    assert "Phase 2 (Reason) received" in messages
    assert "Phase 2 (Reason) produced" in messages
    assert "Phase 3 (Act) received" in messages
    assert "Phase 3 (Act) produced" in messages
    assert "Loop cycle start" in messages
    assert "Loop cycle complete" in messages
