import httpx
import logging

from medelement_mis.client import MedelementClient
from medelement_mis.exceptions import AuthenticationError
from medelement_mis.models import SearchPatientResponse, UpdatePatientResponse


def test_update_patient_calls_put_and_parses_response(monkeypatch):
    client = MedelementClient(
        base_url="https://api3.medelement.com",
        user_id="user",
        password="pass",
        integrator_key="key",
    )

    captured = {}

    def fake_request(method, path, **kwargs):  # noqa: ANN001
        captured["method"] = method
        captured["path"] = path
        captured["json"] = kwargs["json"]
        request = httpx.Request(method, f"{client.base_url}{path}")
        return httpx.Response(
            201,
            request=request,
            json={
                "profile_code": 123,
                "name": "John",
                "lastname": "Golt",
                "social_status": 11,
                "benefit": 15,
            },
        )

    monkeypatch.setattr(client._http, "request", fake_request)

    result = client.patients.update(
        profile_code=123,
        name="John",
        lastname="Golt",
        social_status=11,
        benefit=15,
    )

    assert isinstance(result, UpdatePatientResponse)
    assert result.profile_code == 123
    assert result.social_status == 11
    assert result.benefit == 15
    assert captured["method"] == "PUT"
    assert captured["path"] == "/doctor/v1/patient"
    assert captured["json"]["profile_code"] == 123
    assert captured["json"]["name"] == "John"


def test_search_patients_accepts_empty_list_response(monkeypatch):
    client = MedelementClient(
        base_url="https://api3.medelement.com",
        user_id="user",
        password="pass",
        integrator_key="key",
    )

    captured = {}

    def fake_request(method, path, **kwargs):  # noqa: ANN001
        captured["method"] = method
        captured["path"] = path
        captured["params"] = kwargs["params"]
        request = httpx.Request(method, f"{client.base_url}{path}")
        return httpx.Response(200, request=request, json=[])

    monkeypatch.setattr(client._http, "request", fake_request)

    result = client.patients.search(name="John")

    assert isinstance(result, SearchPatientResponse)
    assert result.root == []
    assert len(result) == 0
    assert captured["method"] == "GET"
    assert captured["path"] == "/doctor/v1/patients"
    assert captured["params"]["name"] == "John"


def test_get_by_iin_calls_v2_patients_with_iin_param(monkeypatch):
    client = MedelementClient(
        base_url="https://api3.medelement.com",
        user_id="user",
        password="pass",
        integrator_key="key",
    )

    captured = {}

    def fake_request(method, path, **kwargs):  # noqa: ANN001
        captured["method"] = method
        captured["path"] = path
        captured["params"] = kwargs["params"]
        request = httpx.Request(method, f"{client.base_url}{path}")
        return httpx.Response(
            200,
            request=request,
            json=[
                {
                    "PROFILE_CODE": 815359391551176579,
                    "NAME": "Дина",
                    "LASTNAME": "Едрисова",
                    "MIDDLENAME": "Test",
                    "BIRTHDAY": "04.10.1965",
                    "GENDER": 2,
                    "IIN": "651004450150",
                    "PATIENT_PHONE_2": ["++7", "777", "7777777", ""],
                    "PATIENT_PHONE_2_STR": "++7 777 7777777",
                    "PHONES_STR": "++7 777 7777777 ",
                }
            ],
        )

    monkeypatch.setattr(client._http, "request", fake_request)

    result = client.patients.get_by_iin("651004450150")

    assert isinstance(result, SearchPatientResponse)
    assert len(result) == 1
    assert result[0].profile_code == 815359391551176579
    assert result[0].iin == "651004450150"
    assert result[0].patient_phone_2 == ["++7", "777", "7777777", ""]
    assert captured["method"] == "GET"
    assert captured["path"] == "/doctor/v2/patients"
    assert captured["params"] == {"iin": "651004450150"}


def test_search_patients_parses_uppercase_api_payload(monkeypatch):
    client = MedelementClient(
        base_url="https://api3.medelement.com",
        user_id="user",
        password="pass",
        integrator_key="key",
    )

    def fake_request(method, path, **kwargs):  # noqa: ANN001
        request = httpx.Request(method, f"{client.base_url}{path}")
        return httpx.Response(
            200,
            request=request,
            json=[
                {
                    "PROFILE_CODE": 9524084,
                    "NAME": "John",
                    "LASTNAME": "Golt",
                    "MIDDLENAME": "",
                    "FULLNAME": "Golt John",
                    "BIRTHDAY": "01.01.1990",
                    "GENDER": 2,
                    "IIN": "",
                    "AMBULATORY_COUNT": 1,
                    "CREATED_DATE": "01.01.2025 10:00:00",
                    "REMOVED": 0,
                    "REMOVED_DATE": "",
                    "REMOVED_REASON": "",
                    "DESCRIPTION": "",
                    "STATUS_CODE": "ACTIVE",
                    "PERCENTAGE_DISCOUNT": 0,
                    "PATIENT_PASSPORT": "",
                    "PATIENT_PHONE_1": "",
                    "PATIENT_PHONE_2": "",
                    "PATIENT_PHONE_3": "",
                    "PATIENT_PHONE_4": "",
                    "PHONES_STR": "",
                    "PATIENT_ADDRESS": "",
                    "PATIENT_EMAIL": "",
                    "PATIENT_CARD_NUMBER": "",
                }
            ],
        )

    monkeypatch.setattr(client._http, "request", fake_request)

    result = client.patients.search(lastname="Golt")

    assert result[0].profile_code == 9524084
    assert result[0].name == "John"
    assert result[0].lastname == "Golt"
    assert result[0].phones_str == ""


def test_search_patients_accepts_phone_array_in_response(monkeypatch):
    client = MedelementClient(
        base_url="https://api3.medelement.com",
        user_id="user",
        password="pass",
        integrator_key="key",
    )

    def fake_request(method, path, **kwargs):  # noqa: ANN001
        request = httpx.Request(method, f"{client.base_url}{path}")
        return httpx.Response(
            200,
            request=request,
            json=[
                {
                    "PROFILE_CODE": 9524084,
                    "NAME": "John",
                    "LASTNAME": "Golt",
                    "PATIENT_PHONE_2": ["+9", "965", "2020664"],
                },
                {
                    "PROFILE_CODE": 9524085,
                    "NAME": "Jane",
                    "LASTNAME": "Golt",
                    "PATIENT_PHONE_2": "",
                },
            ],
        )

    monkeypatch.setattr(client._http, "request", fake_request)

    result = client.patients.search(lastname="Golt")

    assert result[0].patient_phone_2 == ["+9", "965", "2020664"]
    assert result[1].patient_phone_2 == ""


def test_client_context_manager_closes_owned_http_client(monkeypatch):
    client = MedelementClient(
        base_url="https://api3.medelement.com",
        user_id="user",
        password="pass",
        integrator_key="key",
    )
    closed = {"value": False}

    def fake_close() -> None:
        closed["value"] = True

    monkeypatch.setattr(client._http, "close", fake_close)

    with client as managed_client:
        assert managed_client is client

    assert closed["value"] is True


def test_raises_authentication_error_for_403_access_denied():
    client = MedelementClient(
        base_url="https://api3.medelement.com",
        user_id="user",
        password="pass",
        integrator_key="key",
    )
    request = httpx.Request("GET", f"{client.base_url}/doctor/v2/patients")
    response = httpx.Response(403, request=request, text="Access denied. Please contact the API provider to request access.")

    try:
        client._raise_for_status(response)
    except AuthenticationError as exc:
        assert str(exc) == "Access denied. Please contact the API provider to request access."
    else:
        raise AssertionError("Expected AuthenticationError for 403 response")


def test_raises_authentication_error_for_401_empty_array_with_integrator_key():
    client = MedelementClient(
        base_url="https://api3.medelement.com",
        user_id="user",
        password="pass",
        integrator_key="key",
    )
    request = httpx.Request("GET", f"{client.base_url}/doctor/v2/patients")
    response = httpx.Response(401, request=request, json=[])

    try:
        client._raise_for_status(response)
    except AuthenticationError as exc:
        assert str(exc) == "401 Unauthorized: [] response received even though X-Integrator-Key was provided."
    else:
        raise AssertionError("Expected AuthenticationError for 401 response")


def test_request_retries_get_on_5xx(monkeypatch):
    client = MedelementClient(
        base_url="https://api3.medelement.com",
        user_id="user",
        password="pass",
        integrator_key="key",
        retries=2,
        retry_backoff=0.01,
    )
    attempts = {"count": 0}

    def fake_request(method, path, **kwargs):  # noqa: ANN001
        attempts["count"] += 1
        request = httpx.Request(method, f"{client.base_url}{path}")
        status_code = 502 if attempts["count"] < 3 else 200
        return httpx.Response(status_code, request=request, json={})

    monkeypatch.setattr(client._http, "request", fake_request)
    monkeypatch.setattr("medelement_mis.client.time.sleep", lambda _: None)

    response = client._request("GET", "/doctor/v2/patients")

    assert response.status_code == 200
    assert attempts["count"] == 3


def test_request_does_not_retry_post_on_5xx(monkeypatch):
    client = MedelementClient(
        base_url="https://api3.medelement.com",
        user_id="user",
        password="pass",
        integrator_key="key",
        retries=2,
        retry_backoff=0.01,
    )
    attempts = {"count": 0}

    def fake_request(method, path, **kwargs):  # noqa: ANN001
        attempts["count"] += 1
        request = httpx.Request(method, f"{client.base_url}{path}")
        return httpx.Response(503, request=request, json={})

    monkeypatch.setattr(client._http, "request", fake_request)
    monkeypatch.setattr("medelement_mis.client.time.sleep", lambda _: None)

    response = client._request("POST", "/doctor/v1/patient", json={})

    assert response.status_code == 503
    assert attempts["count"] == 1


def test_request_logging_emits_method_url_and_status(caplog):
    client = MedelementClient(
        base_url="https://api3.medelement.com",
        user_id="user",
        password="pass",
        integrator_key="key",
    )
    request = httpx.Request("GET", "https://api3.medelement.com/doctor/v2/patients")
    response = httpx.Response(200, request=request, json=[])

    with caplog.at_level(logging.DEBUG, logger="medelement_mis"):
        client._log_request(request)
        client._log_response(response)

    assert "HTTP request GET https://api3.medelement.com/doctor/v2/patients" in caplog.text
    assert "HTTP response GET https://api3.medelement.com/doctor/v2/patients -> 200" in caplog.text
