"""Chaos — Async parity: every sync resource has a working async counterpart."""

from __future__ import annotations

import httpx
import pytest
import respx

from kanoniv.client.client import KanonivAsyncClient

from .conftest import API_KEY, BASE_URL


@pytest.fixture()
def ac(mock_api: respx.Router):
    """Async client for parity tests."""
    return KanonivAsyncClient(api_key=API_KEY, base_url=BASE_URL, max_retries=0)


# ---------------------------------------------------------------------------
# Sources
# ---------------------------------------------------------------------------

class TestSourcesAsync:
    @pytest.mark.asyncio
    async def test_list(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.get("/v1/sources").mock(
            return_value=httpx.Response(200, json=[{"id": "s1", "name": "crm"}]),
        )
        result = await ac.sources.list()
        assert result[0]["id"] == "s1"

    @pytest.mark.asyncio
    async def test_create(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.post("/v1/sources").mock(
            return_value=httpx.Response(201, json={"id": "s-new", "name": "erp"}),
        )
        result = await ac.sources.create(name="erp", source_type="webhook")
        assert result["id"] == "s-new"

    @pytest.mark.asyncio
    async def test_delete(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.delete("/v1/sources/s1").mock(
            return_value=httpx.Response(204),
        )
        result = await ac.sources.delete("s1")
        assert result is None


# ---------------------------------------------------------------------------
# Rules
# ---------------------------------------------------------------------------

class TestRulesAsync:
    @pytest.mark.asyncio
    async def test_list(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.get("/v1/rules").mock(
            return_value=httpx.Response(200, json=[{"name": "email_exact"}]),
        )
        result = await ac.rules.list()
        assert len(result) == 1

    @pytest.mark.asyncio
    async def test_create(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.post("/v1/rules").mock(
            return_value=httpx.Response(
                201, json={"name": "phone_fuzzy", "rule_type": "fuzzy"},
            ),
        )
        result = await ac.rules.create(
            name="phone_fuzzy", rule_type="fuzzy", config={"threshold": 0.8},
        )
        assert result["name"] == "phone_fuzzy"


# ---------------------------------------------------------------------------
# Jobs
# ---------------------------------------------------------------------------

class TestJobsAsync:
    @pytest.mark.asyncio
    async def test_list(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.get("/v1/jobs").mock(
            return_value=httpx.Response(200, json=[{"id": "j1", "status": "completed"}]),
        )
        result = await ac.jobs.list()
        assert result[0]["id"] == "j1"

    @pytest.mark.asyncio
    async def test_run(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.post("/v1/jobs/run").mock(
            return_value=httpx.Response(200, json={"id": "j-new", "status": "pending"}),
        )
        result = await ac.jobs.run("reconciliation")
        assert result["status"] == "pending"

    @pytest.mark.asyncio
    async def test_cancel(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.post("/v1/jobs/j1/cancel").mock(
            return_value=httpx.Response(200, json={"id": "j1", "status": "cancelled"}),
        )
        result = await ac.jobs.cancel("j1")
        assert result["status"] == "cancelled"


# ---------------------------------------------------------------------------
# Reviews
# ---------------------------------------------------------------------------

class TestReviewsAsync:
    @pytest.mark.asyncio
    async def test_list(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.get("/v1/resolve/pending").mock(
            return_value=httpx.Response(200, json=[{"entity_a_id": "e1"}]),
        )
        result = await ac.reviews.list()
        assert len(result) == 1

    @pytest.mark.asyncio
    async def test_decide(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.post("/v1/resolve/quick").mock(
            return_value=httpx.Response(200, json={"decision": "merge"}),
        )
        result = await ac.reviews.decide(
            entity_a_id="e1", entity_b_id="e2", decision="merge",
        )
        assert result["decision"] == "merge"


# ---------------------------------------------------------------------------
# Overrides
# ---------------------------------------------------------------------------

class TestOverridesAsync:
    @pytest.mark.asyncio
    async def test_list(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.get("/v1/overrides").mock(
            return_value=httpx.Response(200, json=[{"id": "o1"}]),
        )
        result = await ac.overrides.list()
        assert result[0]["id"] == "o1"

    @pytest.mark.asyncio
    async def test_create(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.post("/v1/overrides").mock(
            return_value=httpx.Response(
                201, json={"id": "o-new", "override_type": "force_merge"},
            ),
        )
        result = await ac.overrides.create(
            override_type="force_merge", entity_a_id="e1", entity_b_id="e2",
        )
        assert result["id"] == "o-new"


# ---------------------------------------------------------------------------
# Audit
# ---------------------------------------------------------------------------

class TestAuditAsync:
    @pytest.mark.asyncio
    async def test_list(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.get("/v1/audit").mock(
            return_value=httpx.Response(
                200, json=[{"id": "a1", "action": "merge"}],
            ),
        )
        result = await ac.audit.list()
        assert result[0]["action"] == "merge"

    @pytest.mark.asyncio
    async def test_entity_trail(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.get("/v1/audit/entity/e1").mock(
            return_value=httpx.Response(200, json=[{"action": "create"}]),
        )
        result = await ac.audit.entity_trail("e1")
        assert result[0]["action"] == "create"


# ---------------------------------------------------------------------------
# Specs
# ---------------------------------------------------------------------------

class TestSpecsAsync:
    @pytest.mark.asyncio
    async def test_list(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.get("/v1/identity/specs").mock(
            return_value=httpx.Response(
                200, json=[{"identity_version": 1, "plan_hash": "abc"}],
            ),
        )
        result = await ac.specs.list()
        assert result[0]["identity_version"] == 1

    @pytest.mark.asyncio
    async def test_get(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.get("/v1/identity/specs/1").mock(
            return_value=httpx.Response(
                200, json={"identity_version": 1, "raw_yaml": "...", "plan_hash": "abc"},
            ),
        )
        result = await ac.specs.get(1)
        assert result["identity_version"] == 1

    @pytest.mark.asyncio
    async def test_ingest(self, mock_api: respx.Router, ac: KanonivAsyncClient):
        mock_api.post("/v1/identity/specs").mock(
            return_value=httpx.Response(200, json={"valid": True, "plan_hash": "xyz"}),
        )
        result = await ac.specs.ingest("api_version: '1'\n", compile=True)
        assert result["valid"] is True
