"""FakeAdapter M3 state-progression tests — get_task and get_task_output."""
from __future__ import annotations

import pytest

from backend.app.services.c2.adapter import C2Error
from backend.app.services.c2.fake import FakeAdapter


@pytest.fixture()
def adapter() -> FakeAdapter:
    return FakeAdapter()


@pytest.fixture()
def adapter_with_task(adapter: FakeAdapter) -> tuple[FakeAdapter, int]:
    tid = adapter.create_task(callback_display_id=1, command="whoami")
    return adapter, tid


class TestFakeAdapterGetTaskProgression:
    def test_first_call_returns_submitted(self, adapter_with_task):
        a, tid = adapter_with_task
        status = a.get_task(tid)
        assert status.status == "submitted"
        assert status.completed is False

    def test_second_call_returns_completed(self, adapter_with_task):
        a, tid = adapter_with_task
        a.get_task(tid)  # first call
        status = a.get_task(tid)  # second call
        assert status.status == "completed"
        assert status.completed is True

    def test_subsequent_calls_stay_completed(self, adapter_with_task):
        a, tid = adapter_with_task
        for _ in range(5):
            a.get_task(tid)
        status = a.get_task(tid)
        assert status.completed is True

    def test_unknown_task_id_returns_submitted_on_first_call(self, adapter):
        """A task ID not created by this instance still goes through submitted→completed."""
        status = adapter.get_task(9999)
        assert status.display_id == 9999
        assert status.status == "submitted"
        assert status.completed is False

    def test_call_counters_are_per_task(self, adapter):
        """Two tasks have independent state — completing one does not affect the other."""
        t1 = adapter.create_task(callback_display_id=1, command="whoami")
        t2 = adapter.create_task(callback_display_id=1, command="ipconfig")

        # Advance t1 to completed via two calls.
        adapter.get_task(t1)
        adapter.get_task(t1)

        # t2 first call should still be submitted.
        s2 = adapter.get_task(t2)
        assert s2.status == "submitted"
        assert s2.completed is False

    def test_instances_are_isolated(self):
        """Per-instance counters — different FakeAdapter instances don't share state."""
        a1 = FakeAdapter()
        a2 = FakeAdapter()

        t1 = a1.create_task(1, "cmd")
        t2 = a2.create_task(1, "cmd")

        a1.get_task(t1)
        a1.get_task(t1)  # a1's task is now completed

        # a2's task with same display_id (both start at 1000) should be independent.
        assert t1 == t2 == 1000
        s2 = a2.get_task(t2)
        assert s2.status == "submitted"


class TestFakeAdapterGetTaskOutput:
    def test_raises_before_completed(self, adapter_with_task):
        a, tid = adapter_with_task
        with pytest.raises(C2Error, match="task not completed"):
            a.get_task_output(tid)

    def test_raises_after_first_get_task_call_only(self, adapter_with_task):
        a, tid = adapter_with_task
        a.get_task(tid)  # first call — still submitted
        with pytest.raises(C2Error, match="task not completed"):
            a.get_task_output(tid)

    def test_returns_output_after_completed(self, adapter_with_task):
        a, tid = adapter_with_task
        a.get_task(tid)
        a.get_task(tid)  # now completed
        output = a.get_task_output(tid)
        assert "whoami" in output
        assert str(tid) in output

    def test_output_format(self, adapter):
        tid = adapter.create_task(callback_display_id=2, command="ipconfig /all")
        adapter.get_task(tid)
        adapter.get_task(tid)
        output = adapter.get_task_output(tid)
        assert output == f"output for task {tid}: ipconfig /all\n"

    def test_unknown_task_raises_c2error(self, adapter):
        """Task ID never created and never polled — not completed → C2Error."""
        with pytest.raises(C2Error, match="task not completed"):
            adapter.get_task_output(9999)
