"""Experiments resource for the MLD API client.

Sub-resources:
    experiments.design_data  — experiment design data (one per experiment, owned by design plugin)
    experiments.analysis     — analysis results (one per experiment per analysis plugin)
"""

from __future__ import annotations

from functools import cached_property
from typing import Any

from mld_sdk.client._http import HTTPClient


class DesignDataAPI:
    """Experiment design data operations.

    Design data is a JSON blob owned by an experiment design plugin.
    Each experiment has at most one design data record.
    """

    def __init__(self, http: HTTPClient) -> None:
        self._http = http

    def get(self, experiment_id: int) -> dict[str, Any]:
        """Get the raw design data.

        Returns: {experiment_id, plugin_id, data, schema_version, updated_at}
        """
        return self._http.get(f"/experiments/{experiment_id}/data")

    def save(
        self,
        experiment_id: int,
        *,
        plugin_id: str,
        data: dict[str, Any],
        schema_version: str = "1.0",
    ) -> dict[str, Any]:
        """Create or update design data."""
        return self._http.put(
            f"/experiments/{experiment_id}/data",
            json={
                "plugin_id": plugin_id,
                "data": data,
                "schema_version": schema_version,
            },
        )

    def delete(self, experiment_id: int) -> None:
        """Delete design data for an experiment."""
        self._http.delete(f"/experiments/{experiment_id}/data")

    def tree(self, experiment_id: int) -> dict[str, Any]:
        """Get design data as a hierarchical tree for display."""
        return self._http.get(f"/experiments/{experiment_id}/data/tree")

    def summary(self, experiment_id: int) -> dict[str, Any]:
        """Get a compact summary of the design data."""
        return self._http.get(f"/experiments/{experiment_id}/data/summary")

    def export(self, experiment_id: int, *, format: str = "json") -> Any:
        """Export design data as JSON or CSV."""
        return self._http.get(f"/experiments/{experiment_id}/data/export", format=format)


class AnalysisAPI:
    """Analysis result operations.

    Analysis results are written by analysis plugins. Each experiment can
    have multiple results, one per plugin (keyed by plugin_id).
    """

    def __init__(self, http: HTTPClient) -> None:
        self._http = http

    def list(self, experiment_id: int) -> list[dict[str, Any]]:
        """List all analysis results for an experiment.

        Returns a list of {experiment_id, plugin_id, result, updated_at}.
        """
        response = self._http.get(f"/experiments/{experiment_id}/results")
        # Server returns {"results": [...]}, unwrap for convenience
        if isinstance(response, dict) and "results" in response:
            return response["results"]
        return response

    def get(self, experiment_id: int, plugin_id: str) -> dict[str, Any]:
        """Get the analysis result from a specific plugin.

        Returns: {experiment_id, plugin_id, result, updated_at}
        """
        return self._http.get(f"/experiments/{experiment_id}/results/{plugin_id}")

    def save(
        self,
        experiment_id: int,
        *,
        plugin_id: str,
        result: dict[str, Any],
    ) -> dict[str, Any]:
        """Create or update an analysis result."""
        return self._http.put(
            f"/experiments/{experiment_id}/results/{plugin_id}",
            json={"result": result},
        )

    def delete(self, experiment_id: int, plugin_id: str) -> None:
        """Delete an analysis result."""
        self._http.delete(f"/experiments/{experiment_id}/results/{plugin_id}")


class ExperimentsAPI:
    """Experiment CRUD operations.

    Sub-resources:
        .design_data  — experiment design data (one per experiment)
        .analysis     — analysis results (one per experiment per plugin)
    """

    def __init__(self, http: HTTPClient) -> None:
        self._http = http

    # -- CRUD --

    def list(
        self,
        *,
        status: str | None = None,
        experiment_type: str | None = None,
        project_id: int | None = None,
        search: str | None = None,
        skip: int = 0,
        limit: int = 100,
        sort_by: str = "created_at",
        sort_order: str = "desc",
    ) -> list[dict[str, Any]]:
        """List experiments with optional filtering and sorting."""
        return self._http.get(
            "/experiments",
            status=status,
            experiment_type=experiment_type,
            project_id=project_id,
            search=search,
            skip=skip,
            limit=limit,
            sort_by=sort_by,
            sort_order=sort_order,
        )

    def get(self, experiment_id: int) -> dict[str, Any]:
        """Get a single experiment by ID."""
        return self._http.get(f"/experiments/{experiment_id}")

    def create(
        self,
        *,
        name: str,
        experiment_type: str = "custom",
        project_id: int | None = None,
        notes: str | None = None,
        tags: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Create a new experiment."""
        body: dict[str, Any] = {
            "name": name,
            "experiment_type": experiment_type,
        }
        if project_id is not None:
            body["project_id"] = project_id
        if notes is not None:
            body["notes"] = notes
        if tags is not None:
            body["tags"] = tags
        return self._http.post("/experiments", json=body)

    def update(self, experiment_id: int, **fields: Any) -> dict[str, Any]:
        """Update an experiment. Pass fields as keyword arguments."""
        return self._http.patch(f"/experiments/{experiment_id}", json=fields)

    def delete(self, experiment_id: int) -> None:
        """Delete an experiment."""
        self._http.delete(f"/experiments/{experiment_id}")

    # -- Sub-resources --

    @cached_property
    def design_data(self) -> DesignDataAPI:
        """Access experiment design data (one per experiment)."""
        return DesignDataAPI(self._http)

    @cached_property
    def analysis(self) -> AnalysisAPI:
        """Access analysis results (one per experiment per plugin)."""
        return AnalysisAPI(self._http)

    # -- Experiment types --

    def list_types(self) -> list[dict[str, Any]]:
        """List available experiment types."""
        return self._http.get("/experiments/experiment-types")

    def next_seq(self, experiment_type: str) -> dict[str, Any]:
        """Get the next sequence number for an experiment type."""
        return self._http.get("/experiments/naming/next-seq", experiment_type=experiment_type)

    # -- Backward-compatible flat methods --
    # These delegate to the sub-resources above.

    def get_data(self, experiment_id: int) -> dict[str, Any]:
        """Get design data. Shorthand for ``design_data.get()``."""
        return self.design_data.get(experiment_id)

    def get_data_tree(self, experiment_id: int) -> dict[str, Any]:
        """Shorthand for ``design_data.tree()``."""
        return self.design_data.tree(experiment_id)

    def get_data_summary(self, experiment_id: int) -> dict[str, Any]:
        """Shorthand for ``design_data.summary()``."""
        return self.design_data.summary(experiment_id)

    def save_data(
        self,
        experiment_id: int,
        *,
        plugin_id: str,
        data: dict[str, Any],
        schema_version: str = "1.0",
    ) -> dict[str, Any]:
        """Shorthand for ``design_data.save()``."""
        return self.design_data.save(
            experiment_id,
            plugin_id=plugin_id,
            data=data,
            schema_version=schema_version,
        )

    def delete_data(self, experiment_id: int) -> None:
        """Shorthand for ``design_data.delete()``."""
        self.design_data.delete(experiment_id)

    def export_data(self, experiment_id: int, *, format: str = "json") -> Any:
        """Shorthand for ``design_data.export()``."""
        return self.design_data.export(experiment_id, format=format)

    def get_results(self, experiment_id: int) -> list[dict[str, Any]]:
        """Shorthand for ``analysis.list()``."""
        return self.analysis.list(experiment_id)

    def get_result(self, experiment_id: int, plugin_id: str) -> dict[str, Any]:
        """Shorthand for ``analysis.get()``."""
        return self.analysis.get(experiment_id, plugin_id)

    def save_result(
        self,
        experiment_id: int,
        *,
        plugin_id: str,
        result: dict[str, Any],
    ) -> dict[str, Any]:
        """Shorthand for ``analysis.save()``."""
        return self.analysis.save(experiment_id, plugin_id=plugin_id, result=result)

    def delete_result(self, experiment_id: int, plugin_id: str) -> None:
        """Shorthand for ``analysis.delete()``."""
        self.analysis.delete(experiment_id, plugin_id)
