"""Tests for sync and async clients using pytest-httpx."""

import pytest
from pytest_httpx import HTTPXMock

from areteos import (
    AreteosAsyncClient,
    AreteosClient,
    Edge,
    ForbiddenError,
    NotFoundError,
    Run,
    RunStatus,
    Step,
    ValidationError,
    Workflow,
)

BASE_URL = "https://areteos.test"
API_KEY = "areteos_test_key"
ENGAGEMENT_ID = "550e8400-0000-0000-0000-000000000001"
WORKFLOW_ID = "660e8400-0000-0000-0000-000000000001"
VERSION_ID = "770e8400-0000-0000-0000-000000000001"
RUN_ID = "880e8400-0000-0000-0000-000000000001"


def _workflow_response(workflow_id=WORKFLOW_ID, version_id=VERSION_ID):
    return {
        "data": {
            "type": "workflows",
            "id": workflow_id,
            "attributes": {
                "name": "Test Workflow",
                "description": "A test",
                "scope_type": "engagement",
                "current_version_id": version_id,
                "engagement_id": ENGAGEMENT_ID,
            },
        }
    }


def _run_response(run_id=RUN_ID, status="pending"):
    return {
        "data": {
            "type": "runs",
            "id": run_id,
            "attributes": {
                "status": status,
                "input": {"text": "hello"},
                "output": {"result": "done"} if status == "completed" else None,
                "error_message": None,
                "pause_requested": False,
                "workflow_version_id": VERSION_ID,
                "engagement_id": ENGAGEMENT_ID,
            },
        }
    }


def _error_response(status_code, title="Error", detail="Something went wrong"):
    return {"errors": [{"status": str(status_code), "title": title, "detail": detail}]}


# --- Sync Client Tests ---


class TestSyncCreateWorkflow:
    def test_create_workflow(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_workflow_response())

        with AreteosClient(
            base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
        ) as client:
            workflow = client.create_workflow(
                "Test Workflow",
                steps=[Step.python("step1", "Step 1", code="print('hi')")],
                edges=[],
                description="A test",
            )

        assert isinstance(workflow, Workflow)
        assert str(workflow.id) == WORKFLOW_ID
        assert str(workflow.current_version_id) == VERSION_ID

        request = httpx_mock.get_requests()[0]
        assert request.method == "POST"
        assert "/workflows/create-with-version" in str(request.url)
        body = request.read()
        assert b'"engagement_id"' in body

    def test_create_workflow_with_edges(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_workflow_response())

        with AreteosClient(
            base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
        ) as client:
            client.create_workflow(
                "Test",
                steps=[
                    Step.python("a", "A", code="print(1)"),
                    Step.agent("b", "B"),
                ],
                edges=[Edge(from_step_id="a", to_step_id="b")],
            )

        request = httpx_mock.get_requests()[0]
        assert b'"from_step_id"' in request.read()


class TestSyncTriggerRun:
    def test_trigger(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_run_response())

        with AreteosClient(
            base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
        ) as client:
            run = client.trigger(VERSION_ID, input={"text": "hello"})

        assert isinstance(run, Run)
        assert run.status == RunStatus.PENDING

    def test_get_run(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_run_response(status="running"))

        with AreteosClient(
            base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
        ) as client:
            run = client.get_run(RUN_ID)

        assert run.status == RunStatus.RUNNING


class TestSyncWaitForRun:
    def test_wait_completes(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_run_response(status="running"))
        httpx_mock.add_response(json=_run_response(status="completed"))

        with AreteosClient(
            base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
        ) as client:
            run = client.wait_for_run(RUN_ID, poll_interval=0.01, timeout=5.0)

        assert run.status == RunStatus.COMPLETED
        assert run.output == {"result": "done"}

    @pytest.mark.httpx_mock(can_send_already_matched_responses=True)
    def test_wait_timeout(self, httpx_mock: HTTPXMock):
        # Always return running (reusable response)
        httpx_mock.add_response(json=_run_response(status="running"))

        with AreteosClient(
            base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
        ) as client:
            from areteos.exceptions import TimeoutError

            with pytest.raises(TimeoutError, match="did not complete"):
                client.wait_for_run(RUN_ID, poll_interval=0.01, timeout=0.05)


class TestSyncErrors:
    def test_403_raises_forbidden(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            status_code=403, json=_error_response(403, "Forbidden", "Access denied")
        )

        with AreteosClient(
            base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
        ) as client:
            with pytest.raises(ForbiddenError) as exc_info:
                client.get_workflow(WORKFLOW_ID)
            assert exc_info.value.status_code == 403

    def test_404_raises_not_found(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(status_code=404, json=_error_response(404, "Not Found"))

        with (
            AreteosClient(
                base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
            ) as client,
            pytest.raises(NotFoundError),
        ):
            client.get_run("nonexistent")

    def test_422_raises_validation(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(status_code=422, json=_error_response(422, "Invalid", "Cyclic DAG"))

        with (
            AreteosClient(
                base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
            ) as client,
            pytest.raises(ValidationError, match="Cyclic DAG"),
        ):
            client.create_workflow("Bad", steps=[])


# --- Async Client Tests ---


class TestAsyncCreateWorkflow:
    @pytest.mark.anyio
    async def test_create_workflow(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_workflow_response())

        async with AreteosAsyncClient(
            base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
        ) as client:
            workflow = await client.create_workflow(
                "Test Workflow",
                steps=[Step.python("step1", "Step 1", code="print('hi')")],
            )

        assert isinstance(workflow, Workflow)
        assert str(workflow.id) == WORKFLOW_ID


class TestAsyncTriggerRun:
    @pytest.mark.anyio
    async def test_trigger(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_run_response())

        async with AreteosAsyncClient(
            base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
        ) as client:
            run = await client.trigger(VERSION_ID, input={"text": "hello"})

        assert run.status == RunStatus.PENDING


class TestAsyncWaitForRun:
    @pytest.mark.anyio
    async def test_wait_completes(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_run_response(status="running"))
        httpx_mock.add_response(json=_run_response(status="completed"))

        async with AreteosAsyncClient(
            base_url=BASE_URL, api_key=API_KEY, engagement_id=ENGAGEMENT_ID
        ) as client:
            run = await client.wait_for_run(RUN_ID, poll_interval=0.01, timeout=5.0)

        assert run.status == RunStatus.COMPLETED
