"""Tests for colony state manager."""

from datetime import datetime, timezone

import pytest

from fliiq.runtime.colony.models import (
    AgentReflection,
    AgentRole,
    ColonyState,
    GovernanceDecision,
    IntelBrief,
    Proposal,
    ProposalStatus,
    ProposalType,
    QAMetrics,
    RiskLevel,
    TimelineEntry,
    Vote,
    VotePosition,
)
from fliiq.runtime.colony.state import StateManager


@pytest.fixture()
def state(tmp_path):
    sm = StateManager(tmp_path)
    sm.ensure_dirs()
    return sm


class TestProposals:
    def test_save_and_load(self, state):
        p = Proposal(
            id="PROP-TEST01",
            title="Test proposal",
            type=ProposalType.BUG_FIX,
            risk=RiskLevel.LOW,
            proposer=AgentRole.QA,
            description="Fix a bug",
        )
        state.save_proposal(p)
        loaded = state.load_proposal("PROP-TEST01")
        assert loaded is not None
        assert loaded.title == "Test proposal"
        assert loaded.proposer == AgentRole.QA

    def test_list_proposals(self, state):
        for i in range(3):
            p = Proposal(
                id=f"PROP-T{i:05d}",
                title=f"Proposal {i}",
                type=ProposalType.CONFIG_CHANGE,
                risk=RiskLevel.LOW,
                proposer=AgentRole.SCOUT,
                status=ProposalStatus.PROPOSED if i < 2 else ProposalStatus.REJECTED,
            )
            state.save_proposal(p)

        all_proposals = state.list_proposals()
        assert len(all_proposals) == 3

        proposed = state.list_proposals(status="proposed")
        assert len(proposed) == 2

        rejected = state.list_proposals(status="rejected")
        assert len(rejected) == 1

    def test_update_status(self, state):
        p = Proposal(
            id="PROP-UPD001",
            title="Update test",
            type=ProposalType.REFACTOR,
            risk=RiskLevel.LOW,
            proposer=AgentRole.RESEARCH,
        )
        state.save_proposal(p)
        state.update_proposal_status("PROP-UPD001", "approved")
        loaded = state.load_proposal("PROP-UPD001")
        assert loaded.status == ProposalStatus.APPROVED

    def test_load_nonexistent(self, state):
        assert state.load_proposal("PROP-NOPE") is None


class TestIntelBriefs:
    def test_save_and_list(self, state):
        b = IntelBrief(
            id="INTEL-T00001",
            title="Test brief",
            source_type="blog",
            summary="A summary",
        )
        state.save_intel_brief(b)
        briefs = state.list_intel_briefs()
        assert len(briefs) == 1
        assert briefs[0].title == "Test brief"

    def test_load_brief(self, state):
        b = IntelBrief(id="INTEL-LOAD1", title="Load test", source_type="arxiv")
        state.save_intel_brief(b)
        loaded = state.load_intel_brief("INTEL-LOAD1")
        assert loaded is not None
        assert loaded.source_type == "arxiv"


class TestVotes:
    def test_save_and_get(self, state):
        v1 = Vote(
            proposal_id="PROP-V00001",
            voter=AgentRole.QA,
            position=VotePosition.OPPOSE,
            reasoning="Not enough tests",
        )
        v2 = Vote(
            proposal_id="PROP-V00001",
            voter=AgentRole.SCOUT,
            position=VotePosition.SUPPORT,
            reasoning="Users want this",
        )
        state.save_vote(v1)
        state.save_vote(v2)
        votes = state.get_votes_for("PROP-V00001")
        assert len(votes) == 2
        voters = {v.voter for v in votes}
        assert AgentRole.QA in voters
        assert AgentRole.SCOUT in voters


class TestReflections:
    def test_append_and_get(self, state):
        for i in range(7):
            r = AgentReflection(
                agent=AgentRole.INTELLIGENCE,
                cycle_number=i,
                observation=f"Obs {i}",
                action_taken=f"Action {i}",
                outcome=f"Outcome {i}",
            )
            state.append_reflection(r)

        # Default limit=5
        reflections = state.get_reflections("intelligence")
        assert len(reflections) == 5
        assert reflections[0].cycle_number == 2  # Last 5 of 7

        # All
        reflections = state.get_reflections("intelligence", limit=10)
        assert len(reflections) == 7

    def test_empty_reflections(self, state):
        assert state.get_reflections("research") == []


class TestDecisions:
    def test_append_and_get(self, state):
        d = GovernanceDecision(
            proposal_id="PROP-D00001",
            outcome=ProposalStatus.APPROVED,
            reasoning="Good evidence",
            commit_sha="abc123",
        )
        state.append_decision(d)
        decisions = state.get_decisions()
        assert len(decisions) == 1
        assert decisions[0].commit_sha == "abc123"


class TestMetrics:
    def test_save_and_get_latest(self, state):
        m1 = QAMetrics(test_count=500, pass_count=500, fail_count=0)
        m2 = QAMetrics(test_count=500, pass_count=498, fail_count=2)
        state.save_metrics(m1)
        state.save_metrics(m2)

        latest = state.get_latest_metrics()
        assert latest.fail_count == 2

    def test_metrics_history(self, state):
        for i in range(3):
            state.save_metrics(QAMetrics(test_count=100, pass_count=100 - i, fail_count=i))
        history = state.get_metrics_history()
        assert len(history) == 3

    def test_no_metrics(self, state):
        assert state.get_latest_metrics() is None


class TestTimeline:
    def test_append_and_get(self, state):
        state.append_timeline(TimelineEntry(agent="intel", action="Scanned web"))
        state.append_timeline(TimelineEntry(agent="research", action="Drafted proposal"))
        timeline = state.get_timeline()
        assert len(timeline) == 2

    def test_timeline_limit(self, state):
        for i in range(10):
            state.append_timeline(TimelineEntry(agent="test", action=f"Action {i}"))
        limited = state.get_timeline(limit=3)
        assert len(limited) == 3
        assert limited[0].action == "Action 7"  # Last 3


class TestColonyState:
    def test_save_and_load(self, state):
        cs = ColonyState(experiment_id=1, branch_name="colony/experiment-001")
        state.save_colony_state(cs)
        loaded = state.load_colony_state()
        assert loaded is not None
        assert loaded.experiment_id == 1
        assert loaded.branch_name == "colony/experiment-001"

    def test_no_state(self, state):
        assert state.load_colony_state() is None


class TestContextBuilders:
    def test_intelligence_context_empty(self, state):
        ctx = state.build_intelligence_context()
        assert "No intelligence briefs" in ctx

    def test_intelligence_context(self, state):
        state.save_intel_brief(IntelBrief(
            id="INTEL-CTX01",
            title="Test brief",
            source_type="blog",
            relevance="direct",
            summary="Important finding",
        ))
        ctx = state.build_intelligence_context()
        assert "INTEL-CTX01" in ctx
        assert "Important finding" in ctx

    def test_proposals_context_empty(self, state):
        ctx = state.build_proposals_context()
        assert "No proposals" in ctx

    def test_qa_context_empty(self, state):
        ctx = state.build_qa_context()
        assert "No QA metrics" in ctx

    def test_qa_context(self, state):
        state.save_metrics(QAMetrics(test_count=100, pass_count=99, fail_count=1, lint_issues=2))
        ctx = state.build_qa_context()
        assert "99/100" in ctx
        assert "2 lint" in ctx
