"""Data Models API for managing CDF data models.

Based on the API specification at:
https://api-docs.cognite.com/20230101/tag/Data-models/operation/createDataModels
"""

import builtins
from collections.abc import Iterable, Sequence
from typing import Any, Literal, overload

from cognite_toolkit._cdf_tk.client.cdf_client import CDFResourceAPI, Endpoint, PagedResponse
from cognite_toolkit._cdf_tk.client.http_client import HTTPClient, ItemsSuccessResponse, RequestMessage, SuccessResponse
from cognite_toolkit._cdf_tk.client.request_classes.filters import DataModelFilter
from cognite_toolkit._cdf_tk.client.resource_classes.data_modeling import (
    DataModelReference,
    DataModelReferenceNoVersion,
    DataModelRequest,
    DataModelResponse,
)
from cognite_toolkit._cdf_tk.client.resource_classes.data_modeling._data_model import DataModelResponseWithViews


class DataModelsAPI(CDFResourceAPI[DataModelResponse]):
    """API for managing CDF data models.

    Data models use an apply/upsert pattern for create and update operations.
    """

    def __init__(self, http_client: HTTPClient) -> None:
        super().__init__(
            http_client=http_client,
            method_endpoint_map={
                "upsert": Endpoint(method="POST", path="/models/datamodels", item_limit=100),
                "retrieve": Endpoint(method="POST", path="/models/datamodels/byids", item_limit=100),
                "delete": Endpoint(method="POST", path="/models/datamodels/delete", item_limit=100),
                "list": Endpoint(method="GET", path="/models/datamodels", item_limit=1000),
            },
        )

    def _validate_page_response(
        self, response: SuccessResponse | ItemsSuccessResponse
    ) -> PagedResponse[DataModelResponse]:
        return PagedResponse[DataModelResponse].model_validate_json(response.body)

    def create(self, items: Sequence[DataModelRequest]) -> list[DataModelResponse]:
        """Apply (create or update) data models in CDF.

        Args:
            items: List of DataModelRequest objects to apply.

        Returns:
            List of applied DataModelResponse objects.
        """
        return self._request_item_response(items, "upsert")

    def update(self, items: Sequence[DataModelRequest]) -> list[DataModelResponse]:
        """Apply (create or update) data models in CDF.

        Args:
            items: List of DataModelRequest objects to apply.
        Returns:
            List of applied DataModelResponse objects.
        """
        return self._request_item_response(items, "upsert")

    @overload
    def retrieve(
        self, items: Sequence[DataModelReferenceNoVersion], inline_views: Literal[True]
    ) -> list[DataModelResponseWithViews]: ...

    @overload
    def retrieve(
        self, items: Sequence[DataModelReferenceNoVersion], inline_views: Literal[False] = False
    ) -> list[DataModelResponse]: ...

    def retrieve(
        self, items: Sequence[DataModelReferenceNoVersion], inline_views: bool = False
    ) -> list[DataModelResponse] | list[DataModelResponseWithViews]:
        """Retrieve data models from CDF.

        Args:
            items: List of DataModelReference objects to retrieve.
            inline_views: Whether to include full view definitions in the response.

        Returns:
            List of retrieved DataModelResponse objects.
        """
        if inline_views:
            response_items: list[DataModelResponseWithViews] = []
            for response in self._chunk_requests(
                items, "retrieve", self._serialize_items, params={"inlineViews": True}
            ):
                response_items.extend(
                    PagedResponse[DataModelResponseWithViews].model_validate_json(response.body).items
                )
            return response_items
        else:
            return self._request_item_response(items, method="retrieve", params={"inlineViews": False})

    def delete(self, items: Sequence[DataModelReference]) -> None:
        """Delete data models from CDF.

        Args:
            items: List of DataModelReference objects to delete.
        """
        self._request_no_response(items, "delete")

    def paginate(
        self,
        filter: DataModelFilter | None = None,
        limit: int = 100,
        cursor: str | None = None,
    ) -> PagedResponse[DataModelResponse]:
        """Get a page of data models from CDF.

        Args:
            filter: DataModelFilter to filter data models.
            limit: Maximum number of data models to return.
            cursor: Cursor for pagination.

        Returns:
            PagedResponse of DataModelResponse objects.
        """
        return self._paginate(
            cursor=cursor,
            limit=limit,
            params=filter.dump() if filter else None,
        )

    def iterate(
        self,
        filter: DataModelFilter | None = None,
        limit: int | None = None,
    ) -> Iterable[list[DataModelResponse]]:
        """Iterate over all data models in CDF.

        Args:
            filter: DataModelFilter to filter data models.
            limit: Maximum total number of data models to return.

        Returns:
            Iterable of lists of DataModelResponse objects.
        """
        return self._iterate(
            limit=limit,
            params=filter.dump() if filter else None,
        )

    @overload
    def list(
        self,
        inline_views: Literal[True],
        filter: DataModelFilter | None = None,
        limit: int | None = None,
    ) -> list[DataModelResponseWithViews]: ...

    @overload
    def list(
        self,
        inline_views: Literal[False] = False,
        filter: DataModelFilter | None = None,
        limit: int | None = None,
    ) -> builtins.list[DataModelResponse]: ...

    def list(
        self,
        inline_views: bool = False,
        filter: DataModelFilter | None = None,
        limit: int | None = None,
    ) -> builtins.list[DataModelResponse] | builtins.list[DataModelResponseWithViews]:
        """List all data models in CDF.

        Args:
            filter: DataModelFilter to filter data models.
            limit: Maximum total number of data models to return.

        Returns:
            List of DataModelResponse objects.
        """
        if not inline_views:
            return self._list(limit=limit, params=filter.dump() if filter else None)
        else:
            response_items: list[DataModelResponseWithViews] = []
            cursor: str | None = None
            total = 0
            endpoint = self._method_endpoint_map["list"]
            while True:
                page_limit = endpoint.item_limit if limit is None else min(limit - total, endpoint.item_limit)
                parameters: dict[str, Any] = (self._filter_out_none_values(filter.dump()) or {}) if filter else {}
                parameters["inlineViews"] = True
                parameters["limit"] = page_limit
                if cursor is not None:
                    parameters["cursor"] = cursor
                request = RequestMessage(
                    endpoint_url=self._make_url(endpoint.path),
                    method=endpoint.method,
                    parameters=parameters,
                    disable_gzip=self._disable_gzip,
                    api_version=self._api_version,
                )
                response = self._http_client.request_single_retries(request).get_success_or_raise()
                page = PagedResponse[DataModelResponseWithViews].model_validate_json(response.body)
                response_items.extend(page.items)
                total += len(page.items)
                if page.next_cursor is None or (limit is not None and total >= limit):
                    break
                cursor = page.next_cursor
            return response_items
