# coding: utf-8
from __future__ import annotations

from typing import Dict, List, NamedTuple, Optional

from supervisely._utils import batched
from supervisely.api.module_api import ApiField, ModuleApi, RemoveableBulkModuleApi
from supervisely.video_annotation.key_id_map import KeyIdMap


class ObjectApi(RemoveableBulkModuleApi):
    """
    Object for :class:`~supervisely.video_annotation.video_annotation.VideoAnnotation`.
    """

    @staticmethod
    def info_sequence():
        """
        NamedTuple ObjectInfo information about Object.

        :Example:

         .. code-block:: python

            ObjectInfo(id=152118,
                       description='',
                       created_at='2021-03-23T13:25:34.705Z',
                       updated_at='2021-03-23T13:25:34.705Z',
                       dataset_id=466642, class_id=2856942,
                       entity_id=198703211,
                       tags=[{'objectId': 152118, 'tagId': 29098694, 'entityId': None, 'id': 40632, 'value': 'grey'}],
                       meta={},
                       created_by_id=16154)
        """
        return [
            ApiField.ID,
            ApiField.DESCRIPTION,
            ApiField.CREATED_AT,
            ApiField.UPDATED_AT,
            ApiField.DATASET_ID,
            ApiField.CLASS_ID,
            ApiField.ENTITY_ID,
            ApiField.TAGS,
            ApiField.META,
            ApiField.CREATED_BY_ID,
        ]

    @staticmethod
    def info_tuple_name():
        """
        Get string name of NamedTuple for class.

        :returns: NamedTuple name.
        :rtype: str

        :Usage Example:

            .. code-block:: python

                import os
                from dotenv import load_dotenv

                import supervisely as sly

                # Load secrets and create API object from .env file (recommended)
                # Learn more here: https://developer.supervisely.com/getting-started/basics-of-authentication
                if sly.is_development():
                    load_dotenv(os.path.expanduser("~/supervisely.env"))

                api = sly.Api.from_env()

                tuple_name = api.video.object.info_tuple_name()
                print(tuple_name) # ObjectInfo
        """

        return "ObjectInfo"

    def get_info_by_id(self, id: int) -> NamedTuple:
        """
        Get Object information by ID.

        :param id: Object ID in Supervisely.
        :type id: int
        :returns: Information about Object.
        :rtype: NamedTuple
        """
        return self._get_info_by_id(id, "annotation-objects.info")

    def get_list(
        self, dataset_id: int, filters: Optional[List[Dict[str, str]]] = None
    ) -> List[NamedTuple]:
        """
        Get list of information about all video Objects for a given dataset ID.

        :param dataset_id: Dataset ID in Supervisely.
        :type dataset_id: int
        :param filters: List of parameters to sort output Objects.
        :type filters: List[dict], optional
        :returns: Information about Objects.
        :rtype: List[NamedTuple]

        :Usage Example:

            .. code-block:: python

                import os
                from dotenv import load_dotenv

                import supervisely as sly

                # Load secrets and create API object from .env file (recommended)
                # Learn more here: https://developer.supervisely.com/getting-started/basics-of-authentication
                if sly.is_development():
                    load_dotenv(os.path.expanduser("~/supervisely.env"))

                api = sly.Api.from_env()

                dataset_id = 466642
                object_infos = api.video.object.get_list(dataset_id)
                print(object_infos)
                # Output: [
                #     [
                #         152118,
                #         "",
                #         "2021-03-23T13:25:34.705Z",
                #         "2021-03-23T13:25:34.705Z",
                #         466642,
                #         2856942,
                #         198703211,
                #         [
                #             {
                #                 "objectId": 152118,
                #                 "tagId": 29098694,
                #                 "entityId": null,
                #                 "id": 40632,
                #                 "value": "grey"
                #             }
                #         ],
                #         {},
                #         16154
                #     ],
                #     [
                #         152119,
                #         "",
                #         "2021-03-23T13:25:34.705Z",
                #         "2021-03-23T13:25:34.705Z",
                #         466642,
                #         2856942,
                #         198703211,
                #         [
                #             {
                #                 "objectId": 152119,
                #                 "tagId": 29098694,
                #                 "entityId": null,
                #                 "id": 40633,
                #                 "value": "wine"
                #             }
                #         ],
                #         {},
                #         16154
                #     ],
                #     [
                #         152120,
                #         "",
                #         "2021-03-23T13:25:34.705Z",
                #         "2021-03-23T13:25:34.705Z",
                #         466642,
                #         2856942,
                #         198703211,
                #         [
                #             {
                #                 "objectId": 152120,
                #                 "tagId": 29098694,
                #                 "entityId": null,
                #                 "id": 40634,
                #                 "value": "beige"
                #             }
                #         ],
                #         {},
                #         16154
                #     ],
                #     [
                #         152121,
                #         "",
                #         "2021-03-23T13:25:34.705Z",
                #         "2021-03-23T13:25:34.705Z",
                #         466642,
                #         2856941,
                #         198703212,
                #         [
                #             {
                #                 "objectId": 152121,
                #                 "tagId": 29098696,
                #                 "entityId": null,
                #                 "id": 40635,
                #                 "value": "juvenile"
                #             }
                #         ],
                #         {},
                #         16154
                #     ],
                #     [
                #         152122,
                #         "",
                #         "2021-03-23T13:25:34.705Z",
                #         "2021-03-23T13:25:34.705Z",
                #         466642,
                #         2856943,
                #         198703211,
                #         [],
                #         {},
                #         16154
                #     ]
                # ]
        """
        return self.get_list_all_pages(
            "annotation-objects.list",
            {ApiField.DATASET_ID: dataset_id, ApiField.FILTER: filters or []},
        )

    def _append_bulk(
        self,
        tag_api,
        entity_id,
        project_id,
        dataset_id,
        objects,
        key_id_map: KeyIdMap = None,
        is_pointcloud=False,
        is_video_multi_view: bool = False,
    ):
        """"""
        if len(objects) == 0:
            return []

        objcls_name_id_map = self._api.object_class.get_name_to_id_map(project_id)

        items = []
        for obj in objects:
            new_obj = {ApiField.CLASS_ID: objcls_name_id_map[obj.obj_class.name]}

            if not is_video_multi_view and not is_pointcloud:
                new_obj[ApiField.ENTITY_ID] = entity_id
            items.append(new_obj)

        response = self._api.post(
            "annotation-objects.bulk.add",
            {ApiField.DATASET_ID: dataset_id, ApiField.ANNOTATION_OBJECTS: items},
        )
        ids = [obj[ApiField.ID] for obj in response.json()]
        KeyIdMap.add_objects_to(key_id_map, [obj.key() for obj in objects], ids)

        # add tags to objects
        tag_api.append_to_objects(entity_id, project_id, objects, key_id_map, is_video_multi_view)

        return ids

    def remove_batch(self, ids, progress_cb=None, batch_size=50):
        """
        Remove objects in batches from the Supervisely server.
        All entity IDs must belong to the same item (for example image, volume).

        :param ids: IDs of objects in Supervisely.
        :type ids: List[int]
        :param progress_cb: Function for control remove progress.
        :type progress_cb: Callable

        :Usage Example:

            .. code-block:: python

                import os
                from dotenv import load_dotenv

                import supervisely as sly

                # Load secrets and create API object from .env file (recommended)
                # Learn more here: https://developer.supervisely.com/getting-started/basics-of-authentication
                if sly.is_development():
                    load_dotenv(os.path.expanduser("~/supervisely.env"))

                api = sly.Api.from_env()

                object_ids = [19369645, 19369646, 19369647]
                api.volume.object.remove_batch(object_ids)
        """
        for ids_batch in batched(ids, batch_size=batch_size):
            self._api.post(
                "annotation-objects.bulk.remove",
                ids_batch,
            )
            if progress_cb is not None:
                progress_cb(len(ids_batch))
