"""Tests for ``mgf.apiprobe.core.response``."""

from __future__ import annotations

import dataclasses

import pytest

from mgf.apiprobe.core.response import Response


def _make(
    *,
    status: int = 200,
    headers: dict[str, str] | None = None,
    body: bytes = b"{}",
    elapsed_ms: float = 12.5,
) -> Response:
    return Response(
        url="https://api.example.com/v1/users",
        status=status,
        headers=headers or {"Content-Type": "application/json"},
        body=body,
        elapsed_ms=elapsed_ms,
    )


class TestResponseHeaders:
    def test_case_insensitive_lookup(self) -> None:
        r = _make(headers={"Content-Type": "application/json"})
        assert r.header("content-type") == "application/json"
        assert r.header("CONTENT-TYPE") == "application/json"
        assert r.header("Content-Type") == "application/json"

    def test_missing_header_returns_default(self) -> None:
        r = _make(headers={"Content-Type": "application/json"})
        assert r.header("X-Missing") is None
        assert r.header("X-Missing", "fallback") == "fallback"


class TestResponseBody:
    def test_json_decode(self) -> None:
        r = _make(body=b'{"id": 1, "name": "alice"}')
        assert r.json() == {"id": 1, "name": "alice"}

    def test_json_decode_error_on_malformed(self) -> None:
        r = _make(body=b"not-json")
        with pytest.raises(ValueError):
            r.json()


class TestStatusCategorization:
    @pytest.mark.parametrize("status", [200, 201, 204, 299])
    def test_is_success(self, status: int) -> None:
        r = _make(status=status)
        assert r.is_success is True
        assert r.is_client_error is False
        assert r.is_server_error is False

    @pytest.mark.parametrize("status", [400, 401, 404, 499])
    def test_is_client_error(self, status: int) -> None:
        r = _make(status=status)
        assert r.is_success is False
        assert r.is_client_error is True
        assert r.is_server_error is False

    @pytest.mark.parametrize("status", [500, 502, 503, 599])
    def test_is_server_error(self, status: int) -> None:
        r = _make(status=status)
        assert r.is_success is False
        assert r.is_client_error is False
        assert r.is_server_error is True

    @pytest.mark.parametrize("status", [100, 199, 300, 399])
    def test_other_status_codes(self, status: int) -> None:
        r = _make(status=status)
        assert r.is_success is False
        assert r.is_client_error is False
        assert r.is_server_error is False


class TestResponseFrozen:
    def test_cannot_mutate_fields(self) -> None:
        r = _make()
        with pytest.raises(dataclasses.FrozenInstanceError):
            r.status = 500  # type: ignore[misc]
