"""Tests for the Experiments resource API."""

import json

import httpx
import pytest

from mld_sdk.client._http import HTTPClient
from mld_sdk.client.resources.experiments import AnalysisAPI, DesignDataAPI, ExperimentsAPI


def _make_experiments_api(handler) -> ExperimentsAPI:
    http = HTTPClient("http://localhost:8001")
    http._client = httpx.Client(
        base_url="http://localhost:8001",
        transport=httpx.MockTransport(handler),
    )
    return ExperimentsAPI(http)


class TestExperimentsList:
    def test_list_returns_experiments(self):
        def handler(request: httpx.Request) -> httpx.Response:
            return httpx.Response(
                200,
                json=[
                    {"id": 1, "name": "EXP-001", "status": "planned"},
                    {"id": 2, "name": "EXP-002", "status": "completed"},
                ],
            )

        api = _make_experiments_api(handler)
        result = api.list()
        assert len(result) == 2
        assert result[0]["name"] == "EXP-001"

    def test_list_passes_filter_params(self):
        requests_made: list[httpx.Request] = []

        def handler(request: httpx.Request) -> httpx.Response:
            requests_made.append(request)
            return httpx.Response(200, json=[])

        api = _make_experiments_api(handler)
        api.list(status="completed", experiment_type="drp", limit=50)

        url = str(requests_made[0].url)
        assert "status=completed" in url
        assert "experiment_type=drp" in url
        assert "limit=50" in url


class TestExperimentsGet:
    def test_get_by_id(self):
        def handler(request: httpx.Request) -> httpx.Response:
            assert "/api/experiments/42" in str(request.url)
            return httpx.Response(
                200,
                json={"id": 42, "name": "EXP-042", "status": "completed"},
            )

        api = _make_experiments_api(handler)
        exp = api.get(42)
        assert exp["id"] == 42
        assert exp["name"] == "EXP-042"


class TestExperimentsCreate:
    def test_create_sends_correct_body(self):
        requests_made: list[httpx.Request] = []

        def handler(request: httpx.Request) -> httpx.Response:
            requests_made.append(request)
            return httpx.Response(201, json={"id": 10, "name": "New Exp"})

        api = _make_experiments_api(handler)
        result = api.create(name="New Exp", experiment_type="custom", notes="test notes")

        assert result["id"] == 10
        body = json.loads(requests_made[0].content)
        assert body["name"] == "New Exp"
        assert body["experiment_type"] == "custom"
        assert body["notes"] == "test notes"


class TestExperimentsUpdate:
    def test_update_sends_patch(self):
        requests_made: list[httpx.Request] = []

        def handler(request: httpx.Request) -> httpx.Response:
            requests_made.append(request)
            return httpx.Response(200, json={"id": 1, "status": "completed"})

        api = _make_experiments_api(handler)
        api.update(1, status="completed")

        assert requests_made[0].method == "PATCH"
        body = json.loads(requests_made[0].content)
        assert body["status"] == "completed"


class TestSubResourceProperties:
    def test_design_data_is_cached_property(self):
        api = _make_experiments_api(lambda r: httpx.Response(200, json={}))
        assert isinstance(api.design_data, DesignDataAPI)
        assert api.design_data is api.design_data

    def test_analysis_is_cached_property(self):
        api = _make_experiments_api(lambda r: httpx.Response(200, json={}))
        assert isinstance(api.analysis, AnalysisAPI)
        assert api.analysis is api.analysis


# --- Design Data sub-resource ---


class TestDesignData:
    def test_get(self):
        def handler(request: httpx.Request) -> httpx.Response:
            assert "/data" in str(request.url)
            assert request.method == "GET"
            return httpx.Response(
                200,
                json={
                    "experiment_id": 1,
                    "plugin_id": "ms-designer",
                    "data": {"samples": [{"name": "S1"}]},
                    "schema_version": "1.0",
                },
            )

        api = _make_experiments_api(handler)
        data = api.design_data.get(1)
        assert data["plugin_id"] == "ms-designer"
        assert data["data"]["samples"][0]["name"] == "S1"

    def test_save(self):
        requests_made: list[httpx.Request] = []

        def handler(request: httpx.Request) -> httpx.Response:
            requests_made.append(request)
            return httpx.Response(200, json={"experiment_id": 1})

        api = _make_experiments_api(handler)
        api.design_data.save(1, plugin_id="ms-designer", data={"key": "value"})

        assert requests_made[0].method == "PUT"
        body = json.loads(requests_made[0].content)
        assert body["plugin_id"] == "ms-designer"
        assert body["data"] == {"key": "value"}
        assert body["schema_version"] == "1.0"

    def test_delete(self):
        requests_made: list[httpx.Request] = []

        def handler(request: httpx.Request) -> httpx.Response:
            requests_made.append(request)
            return httpx.Response(200, json={"status": "deleted"})

        api = _make_experiments_api(handler)
        api.design_data.delete(1)
        assert requests_made[0].method == "DELETE"
        assert "/data" in str(requests_made[0].url)

    def test_tree(self):
        def handler(request: httpx.Request) -> httpx.Response:
            assert "/data/tree" in str(request.url)
            return httpx.Response(200, json={"tree": {"root": {}}, "plugin_id": "ms-designer"})

        api = _make_experiments_api(handler)
        result = api.design_data.tree(1)
        assert "tree" in result

    def test_summary(self):
        def handler(request: httpx.Request) -> httpx.Response:
            assert "/data/summary" in str(request.url)
            return httpx.Response(200, json={"summary": {}, "plugin_id": "ms-designer"})

        api = _make_experiments_api(handler)
        result = api.design_data.summary(1)
        assert "summary" in result

    def test_export_passes_format(self):
        requests_made: list[httpx.Request] = []

        def handler(request: httpx.Request) -> httpx.Response:
            requests_made.append(request)
            return httpx.Response(200, json={"csv": "data"})

        api = _make_experiments_api(handler)
        api.design_data.export(1, format="csv")

        url = str(requests_made[0].url)
        assert "format=csv" in url
        assert "/data/export" in url


# --- Analysis Results sub-resource ---


class TestAnalysis:
    def test_list_unwraps_results_wrapper(self):
        """Server returns {"results": [...]}, client unwraps to bare list."""

        def handler(request: httpx.Request) -> httpx.Response:
            return httpx.Response(
                200,
                json={
                    "results": [
                        {"plugin_id": "drp", "result": {"score": 0.95}},
                        {"plugin_id": "ms-uploader", "result": {"files": 3}},
                    ],
                },
            )

        api = _make_experiments_api(handler)
        results = api.analysis.list(1)
        assert isinstance(results, list)
        assert len(results) == 2
        assert results[0]["plugin_id"] == "drp"

    def test_get_by_plugin(self):
        def handler(request: httpx.Request) -> httpx.Response:
            assert "/results/drp" in str(request.url)
            return httpx.Response(
                200,
                json={
                    "experiment_id": 1,
                    "plugin_id": "drp",
                    "result": {"score": 0.95},
                },
            )

        api = _make_experiments_api(handler)
        result = api.analysis.get(1, "drp")
        assert result["plugin_id"] == "drp"
        assert result["result"]["score"] == 0.95

    def test_save(self):
        requests_made: list[httpx.Request] = []

        def handler(request: httpx.Request) -> httpx.Response:
            requests_made.append(request)
            return httpx.Response(200, json={"plugin_id": "drp"})

        api = _make_experiments_api(handler)
        api.analysis.save(1, plugin_id="drp", result={"prediction": [1, 2, 3]})

        assert requests_made[0].method == "PUT"
        body = json.loads(requests_made[0].content)
        assert body["result"] == {"prediction": [1, 2, 3]}

    def test_delete(self):
        requests_made: list[httpx.Request] = []

        def handler(request: httpx.Request) -> httpx.Response:
            requests_made.append(request)
            return httpx.Response(200, json={"status": "deleted"})

        api = _make_experiments_api(handler)
        api.analysis.delete(1, "drp")
        assert requests_made[0].method == "DELETE"
        assert "/results/drp" in str(requests_made[0].url)


# --- Backward-compatible flat methods ---


class TestBackwardCompatFlatMethods:
    """Flat methods on ExperimentsAPI delegate to sub-resources."""

    def test_get_data_delegates_to_design_data(self):
        def handler(request: httpx.Request) -> httpx.Response:
            return httpx.Response(200, json={"plugin_id": "x", "data": {}})

        api = _make_experiments_api(handler)
        assert api.get_data(1) == api.design_data.get(1)

    def test_get_results_delegates_to_analysis(self):
        def handler(request: httpx.Request) -> httpx.Response:
            return httpx.Response(200, json={"results": [{"plugin_id": "drp"}]})

        api = _make_experiments_api(handler)
        result = api.get_results(1)
        assert isinstance(result, list)
        assert result[0]["plugin_id"] == "drp"

    def test_save_data_delegates(self):
        requests_made: list[httpx.Request] = []

        def handler(request: httpx.Request) -> httpx.Response:
            requests_made.append(request)
            return httpx.Response(200, json={})

        api = _make_experiments_api(handler)
        api.save_data(1, plugin_id="x", data={"k": "v"})
        body = json.loads(requests_made[0].content)
        assert body["plugin_id"] == "x"

    def test_save_result_delegates(self):
        requests_made: list[httpx.Request] = []

        def handler(request: httpx.Request) -> httpx.Response:
            requests_made.append(request)
            return httpx.Response(200, json={})

        api = _make_experiments_api(handler)
        api.save_result(1, plugin_id="drp", result={"v": 1})
        body = json.loads(requests_made[0].content)
        assert body["result"] == {"v": 1}
