"""
Datasets API client for managing datasets in Mammoth.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from ..client import MammothClient

_list = list  # Alias to avoid shadowing by method name


class DatasetsAPI:
    """Client for interacting with Mammoth Datasets API.

    Access via client.datasets:
        datasets = client.datasets.list()
        dataset = client.datasets.get(123)
        data = client.datasets.get_data(123)
    """

    def __init__(self, client: MammothClient) -> None:
        self._client = client

    def _ws(self) -> int:
        return self._client.workspace_id

    def _proj(self, project_id: int | None = None) -> int:
        if project_id is not None:
            return project_id
        proj = getattr(self._client, "project_id", None)
        if proj is not None:
            return proj
        raise ValueError("project_id must be set on the client using client.set_project_id()")

    def list(
        self,
        workspace_id: int | None = None,
        project_id: int | None = None,
        limit: int = 100,
        sort: str = "(created_at:desc)",
    ) -> dict[str, Any]:
        """Get list of datasets in a project.

        Args:
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).
            limit: Maximum number of results (default 100).
            sort: Sort order (default "(created_at:desc)").

        Returns:
            Dict containing datasets list with id, name and other info.
        """
        ws = workspace_id or self._ws()
        proj = self._proj(project_id)
        params = {"fields": "id,name", "limit": limit, "sort": sort}
        return self._client._request_json(
            "GET", f"/workspaces/{ws}/projects/{proj}/datasets", params=params
        )

    def get(
        self,
        dataset_id: int,
        workspace_id: int | None = None,
        project_id: int | None = None,
    ) -> dict[str, Any]:
        """Get dataset details by ID.

        Args:
            dataset_id: ID of the dataset.
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).

        Returns:
            Dict with complete dataset information.
        """
        ws = workspace_id or self._ws()
        proj = self._proj(project_id)
        return self._client._request_json(
            "GET", f"/workspaces/{ws}/projects/{proj}/datasets/{dataset_id}"
        )

    def get_data(
        self,
        dataset_id: int,
        workspace_id: int | None = None,
        project_id: int | None = None,
        timeout: int = 300,
        poll_interval: int = 2,
    ) -> dict[str, Any]:
        """Get the actual data from a dataset. Polls the job until completion.

        Args:
            dataset_id: ID of the dataset.
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).
            timeout: Maximum wait time in seconds (default 300).
            poll_interval: Polling interval in seconds (default 2).

        Returns:
            Dict with dataset data.
        """
        ws = workspace_id or self._ws()
        proj = self._proj(project_id)

        response = self._client._request_json(
            "GET", f"/workspaces/{ws}/projects/{proj}/datasets/{dataset_id}/data"
        )
        return self._client._wait_if_job(response, timeout=timeout, poll_interval=poll_interval)

    def create(
        self,
        dataset_spec: dict[str, Any],
        ds_creation_type: str,
        folder_resource_id: str | None = None,
        workspace_id: int | None = None,
        project_id: int | None = None,
    ) -> dict[str, Any]:
        """Create a new dataset.

        Args:
            dataset_spec: Dataset specification (varies by creation type).
            ds_creation_type: Type of creation: "clone", "cloud", "sketch", "weburl".
            folder_resource_id: Optional folder resource ID.
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).

        Returns:
            Dict with created dataset information.
        """
        ws = workspace_id or self._ws()
        proj = self._proj(project_id)

        payload: dict[str, Any] = {
            "dataset_spec": dataset_spec,
            "ds_creation_type": ds_creation_type,
        }
        if folder_resource_id is not None:
            payload["folder_resource_id"] = folder_resource_id

        return self._client._request_json(
            "POST", f"/workspaces/{ws}/projects/{proj}/datasets", json=payload
        )

    def update(
        self,
        patch_data: _list[dict[str, Any]],
        workspace_id: int | None = None,
        project_id: int | None = None,
    ) -> dict[str, Any]:
        """Update datasets using JSON Patch operations.

        The server expects patch operations sent to the plural ``/datasets``
        endpoint. Each operation must include ``op``, ``path``, and ``value``.

        Supported operations (mapped via ``OP_PATCH_TO_FUNCTION_MAP`` on the
        backend): ``rename_dataset``, ``update_datasets``, ``delete_datasets``,
        ``change_ds_column_type``, ``add_columns``, ``remove_columns``,
        ``rename_column``, ``refresh_data``, ``reattach_connection``.

        Args:
            patch_data: List of patch operations.
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).

        Returns:
            Dict with update result.

        Example::

            # Rename a dataset
            client.datasets.update([
                {"op": "rename_dataset", "path": "/123", "value": {"name": "New Name"}}
            ])
        """
        ws = workspace_id or self._ws()
        proj = self._proj(project_id)
        return self._client._request_json(
            "PATCH",
            f"/workspaces/{ws}/projects/{proj}/datasets",
            json={"patch": patch_data},
        )

    def rename(
        self,
        dataset_id: int,
        name: str,
        workspace_id: int | None = None,
        project_id: int | None = None,
    ) -> dict[str, Any]:
        """Rename a dataset.

        Convenience method wrapping :meth:`update` with a ``rename_dataset``
        patch operation.

        Args:
            dataset_id: ID of the dataset to rename.
            name: New name for the dataset.
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).

        Returns:
            Dict with update result.
        """
        return self.update(
            [{"op": "rename_dataset", "path": f"/{dataset_id}", "value": {"name": name}}],
            workspace_id=workspace_id,
            project_id=project_id,
        )

    def delete(
        self,
        dataset_id: int,
        workspace_id: int | None = None,
        project_id: int | None = None,
    ) -> None:
        """Delete a dataset.

        Args:
            dataset_id: ID of the dataset to delete.
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).
        """
        ws = workspace_id or self._ws()
        proj = self._proj(project_id)
        self._client._request_json(
            "DELETE", f"/workspaces/{ws}/projects/{proj}/datasets/{dataset_id}"
        )

    def bulk_update(
        self,
        patch_data: dict[str, Any],
        workspace_id: int | None = None,
        project_id: int | None = None,
    ) -> dict[str, Any]:
        """Update multiple datasets (bulk operation).

        Args:
            patch_data: Patch operation data for multiple datasets.
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).

        Returns:
            Dict with bulk update result.
        """
        ws = workspace_id or self._ws()
        proj = self._proj(project_id)
        return self._client._request_json(
            "PATCH", f"/workspaces/{ws}/projects/{proj}/datasets", json={"patch": patch_data}
        )

    def bulk_delete(
        self,
        workspace_id: int | None = None,
        project_id: int | None = None,
    ) -> None:
        """Delete multiple datasets (bulk operation).

        Args:
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).
        """
        ws = workspace_id or self._ws()
        proj = self._proj(project_id)
        self._client._request_json("DELETE", f"/workspaces/{ws}/projects/{proj}/datasets")

    def list_batches(
        self,
        dataset_id: int,
        workspace_id: int | None = None,
        project_id: int | None = None,
    ) -> _list[dict[str, Any]]:
        """List batches for a dataset.

        Args:
            dataset_id: ID of the dataset.
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).

        Returns:
            List of batch dicts.
        """
        ws = workspace_id or self._ws()
        proj = self._proj(project_id)
        response = self._client._request_json(
            "GET", f"/workspaces/{ws}/projects/{proj}/datasets/{dataset_id}/batches"
        )
        return response.get("batches", response if isinstance(response, _list) else [])

    def get_batch(
        self,
        dataset_id: int,
        batch_id: int,
        workspace_id: int | None = None,
        project_id: int | None = None,
    ) -> dict[str, Any]:
        """Get details of a specific batch.

        Args:
            dataset_id: ID of the dataset.
            batch_id: ID of the batch.
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).

        Returns:
            Dict with batch details.
        """
        ws = workspace_id or self._ws()
        proj = self._proj(project_id)
        return self._client._request_json(
            "GET", f"/workspaces/{ws}/projects/{proj}/datasets/{dataset_id}/batches/{batch_id}"
        )

    def get_file_settings(
        self,
        dataset_id: int,
        workspace_id: int | None = None,
        project_id: int | None = None,
    ) -> dict[str, Any]:
        """Get file settings for a dataset.

        Args:
            dataset_id: ID of the dataset.
            workspace_id: ID of the workspace (uses client default if not provided).
            project_id: ID of the project (uses client default if not provided).

        Returns:
            Dict with file settings.
        """
        ws = workspace_id or self._ws()
        proj = self._proj(project_id)
        return self._client._request_json(
            "GET", f"/workspaces/{ws}/projects/{proj}/datasets/{dataset_id}/file_settings"
        )
