from collections.abc import Sequence

from cognite_toolkit._cdf_tk.client.api.instances import MultiWrappedInstancesAPI, WrappedInstancesAPI
from cognite_toolkit._cdf_tk.client.cdf_client import PagedResponse, ResponseItems
from cognite_toolkit._cdf_tk.client.http_client import (
    HTTPClient,
    ItemsSuccessResponse,
    SuccessResponse,
)
from cognite_toolkit._cdf_tk.client.identifiers import InstanceDefinitionId, NodeId
from cognite_toolkit._cdf_tk.client.resource_classes.apm_config_v1 import (
    APM_CONFIG_SPACE,
    APMConfigRequest,
    APMConfigResponse,
)
from cognite_toolkit._cdf_tk.client.resource_classes.data_modeling._query import (
    QueryNodeExpression,
    QueryNodeTableExpression,
    QueryRequest,
    QueryResponseUntyped,
    QuerySelect,
    QuerySelectSource,
    QueryThrough,
)
from cognite_toolkit._cdf_tk.client.resource_classes.infield import (
    DataExplorationConfig,
    InFieldCDMLocationConfigRequest,
    InFieldCDMLocationConfigResponse,
    InFieldLocationConfig,
    InFieldLocationConfigRequest,
    InFieldLocationConfigResponse,
)


class InfieldConfigAPI(MultiWrappedInstancesAPI[InFieldLocationConfigRequest, InFieldLocationConfigResponse]):
    _LOCATION_REF = "locationConfig"
    _EXPLORATION_REF = "dataExplorationConfig"

    def __init__(self, http_client: HTTPClient) -> None:
        # 500 is chosen as 1000 is the maximum for nodes, and each location config consists of 1 or 2 nodes
        super().__init__(http_client, query_chunk=500)

    def _retrieve_query(self, items: Sequence[InstanceDefinitionId]) -> QueryRequest:
        return QueryRequest(
            with_={
                self._LOCATION_REF: QueryNodeExpression(
                    limit=len(items),
                    nodes=QueryNodeTableExpression(
                        filter={"instanceReferences": [item.dump(include_instance_type=False) for item in items]},
                    ),
                ),
                self._EXPLORATION_REF: QueryNodeExpression(
                    nodes=QueryNodeTableExpression(
                        from_="locationConfig",
                        direction="outwards",
                        through=QueryThrough(
                            source=InFieldLocationConfig.VIEW_ID,
                            identifier="dataExplorationConfig",
                        ),
                    ),
                ),
            },
            select={
                self._LOCATION_REF: QuerySelect(
                    sources=[QuerySelectSource(source=InFieldLocationConfig.VIEW_ID, properties=["*"])],
                ),
                self._EXPLORATION_REF: QuerySelect(
                    sources=[QuerySelectSource(source=DataExplorationConfig.VIEW_ID, properties=["*"])],
                ),
            },
        )

    def _validate_query_response(self, query_response: QueryResponseUntyped) -> list[InFieldLocationConfigResponse]:
        exploration_config_results = (
            DataExplorationConfig.model_validate(item) for item in query_response.items.get(self._EXPLORATION_REF, [])
        )
        exploration_config_map = {(item.space, item.external_id): item for item in exploration_config_results}
        results: list[InFieldLocationConfigResponse] = []
        for item in query_response.items.get(self._LOCATION_REF, []):
            location_config = InFieldLocationConfigResponse.model_validate(item)
            exploration_config = location_config.data_exploration_config
            if exploration_config is not None:
                location_config.data_exploration_config = exploration_config_map.get(
                    (exploration_config.space, exploration_config.external_id), exploration_config
                )
            results.append(location_config)
        return results


class InFieldCDMConfigAPI(WrappedInstancesAPI[NodeId, InFieldCDMLocationConfigResponse]):
    def __init__(self, http_client: HTTPClient) -> None:
        super().__init__(http_client, InFieldCDMLocationConfigRequest.VIEW_ID)

    def _validate_response(self, response: SuccessResponse) -> ResponseItems[NodeId]:
        return ResponseItems[NodeId].model_validate_json(response.body)

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

    def list(self, limit: int | None = 100) -> list[InFieldCDMLocationConfigResponse]:
        """List all in-field CDM configs.

        Args:
            limit: Maximum number of items to return. If None, all items are returned.
        Returns:
            List of InFieldCDMLocationConfigResponse objects.
        """
        return super()._list_instances(instance_type="node", limit=limit)


class APMConfigAPI(WrappedInstancesAPI[NodeId, APMConfigResponse]):
    def __init__(self, http_client: HTTPClient) -> None:
        super().__init__(http_client, APMConfigRequest.VIEW_ID)

    def _validate_response(self, response: SuccessResponse) -> ResponseItems[NodeId]:
        return ResponseItems[NodeId].model_validate_json(response.body)

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

    def list(self, limit: int | None = 100) -> list[APMConfigResponse]:
        """List all APM configs.

        Args:
            limit: Maximum number of items to return. If None, all items are returned.

        Returns:
            List of APMConfigResponse objects.
        """
        return super()._list_instances(spaces=[APM_CONFIG_SPACE], instance_type="node", limit=limit)


class InfieldAPI:
    def __init__(self, http_client: HTTPClient) -> None:
        self._http_client = http_client
        self.apm_config = APMConfigAPI(http_client)
        self.config = InfieldConfigAPI(http_client)
        self.cdm_config = InFieldCDMConfigAPI(http_client)
