"""Additional coverage for api.py - context manager, health with details,
empty response bodies, 403 auth errors, report_health standalone."""

from __future__ import annotations

from unittest.mock import MagicMock, patch

import pytest
from pytest_httpx import HTTPXMock

from cli.api import BeaconAPI, report_health
from cli.config import Settings
from cli.exceptions import BeaconAPIError, BeaconAuthError

from .conftest import SAMPLE_BEACON

BASE_URL = "https://api.test.ethiack.com"


@pytest.fixture()
def api(settings) -> BeaconAPI:
    return BeaconAPI(settings)


class TestContextManager:
    def test_enter_returns_self(self, settings):
        api = BeaconAPI(settings)
        result = api.__enter__()
        assert result is api
        api.__exit__(None, None, None)

    def test_exit_closes_client(self, settings):
        api = BeaconAPI(settings)
        api.__enter__()
        api.__exit__(None, None, None)
        # Calling close again should not raise
        api.close()

    def test_can_be_used_as_context_manager(self, settings, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            method="GET",
            url=f"{BASE_URL}/v1/organizations/test-org-id/beacons/test-id",
            json=SAMPLE_BEACON,
        )
        with BeaconAPI(settings) as api:
            result = api.get("test-id")
        assert result["id"] == "beacon-id-001"


class TestHealthWithDetails:
    def test_health_sends_details(self, api, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            method="POST",
            url=f"{BASE_URL}/v1/beacons/health",
            status_code=200,
        )
        api.health(
            beacon_id="bid-001",
            status="faulty",
            beacon_token="tok",
            details={"wireguard": {"active": False}, "rathole_running": True},
        )
        import json
        request = httpx_mock.get_request()
        body = json.loads(request.content)
        assert body["details"]["wireguard"]["active"] is False

    def test_health_without_details(self, api, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            method="POST",
            url=f"{BASE_URL}/v1/beacons/health",
            status_code=200,
        )
        api.health(beacon_id="bid", status="active", beacon_token="tok")
        import json
        body = json.loads(httpx_mock.get_request().content)
        assert "details" not in body


class TestResponseEdgeCases:
    def test_403_raises_auth_error(self, api, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            method="GET",
            url=f"{BASE_URL}/v1/organizations/test-org-id/beacons/x",
            status_code=403,
        )
        with pytest.raises(BeaconAuthError):
            api.get("x")

    def test_empty_response_body_returns_empty_dict(self, api, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            method="DELETE",
            url=f"{BASE_URL}/v1/organizations/test-org-id/beacons/x",
            status_code=204,
            content=b"",
        )
        result = api.delete("x")
        assert result == {} or result is None

    def test_non_json_error_response_uses_text(self, api, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            method="GET",
            url=f"{BASE_URL}/v1/organizations/test-org-id/beacons/x",
            status_code=500,
            content=b"Internal Server Error",
            headers={"content-type": "text/plain"},
        )
        with pytest.raises(BeaconAPIError, match="Internal Server Error"):
            api.get("x")

    def test_201_response_accepted_for_post(self, api, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            method="POST",
            url=f"{BASE_URL}/v1/organizations/test-org-id/beacons/",
            json=SAMPLE_BEACON,
            status_code=201,
        )
        result = api.create("n", ["10.0.0.0/8"])
        assert result["id"] == "beacon-id-001"


# ---------------------------------------------------------------------------
# report_health - standalone module-level function
# ---------------------------------------------------------------------------


class TestReportHealth:
    def test_posts_status_without_details(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            method="POST",
            url=f"{BASE_URL}/v1/beacons/health",
            status_code=200,
        )
        report_health(BASE_URL, "bid-001", "active", "tok-abc")
        import json
        body = json.loads(httpx_mock.get_request().content)
        assert body["beacon_id"] == "bid-001"
        assert body["status"] == "active"
        assert "details" not in body

    def test_posts_status_with_details(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            method="POST",
            url=f"{BASE_URL}/v1/beacons/health",
            status_code=200,
        )
        details = {"wireguard": {"active": True}, "rathole_running": True}
        report_health(BASE_URL, "bid-002", "active", "tok-abc", details=details)
        import json
        body = json.loads(httpx_mock.get_request().content)
        assert body["details"]["wireguard"]["active"] is True

    def test_raises_beacon_api_error_on_connect_error(self):
        import httpx
        with patch("httpx.Client") as mock_client_cls:
            mock_client = MagicMock()
            mock_client.__enter__ = lambda s: mock_client
            mock_client.__exit__ = MagicMock(return_value=False)
            mock_client.post.side_effect = httpx.ConnectError("refused")
            mock_client_cls.return_value = mock_client
            with pytest.raises(BeaconAPIError, match="Could not connect"):
                report_health("https://api.test.ethiack.com", "bid", "active", "tok")

    def test_raises_beacon_api_error_on_timeout(self):
        import httpx
        with patch("httpx.Client") as mock_client_cls:
            mock_client = MagicMock()
            mock_client.__enter__ = lambda s: mock_client
            mock_client.__exit__ = MagicMock(return_value=False)
            mock_client.post.side_effect = httpx.TimeoutException("timeout")
            mock_client_cls.return_value = mock_client
            with pytest.raises(BeaconAPIError, match="timed out"):
                report_health("https://api.test.ethiack.com", "bid", "active", "tok")

    def test_uses_beacon_token_header(self, httpx_mock: HTTPXMock):
        httpx_mock.add_response(
            method="POST",
            url=f"{BASE_URL}/v1/beacons/health",
            status_code=200,
        )
        report_health(BASE_URL, "bid-003", "faulty", "my-secret-token")
        request = httpx_mock.get_request()
        assert request.headers["x-beacon-token"] == "my-secret-token"
