# Copyright (c) 2022 CloudZero - ALL RIGHTS RESERVED - PROPRIETARY AND CONFIDENTIAL
# Unauthorized copying of this file and/or project, via any medium is strictly prohibited.
# Direct all questions to legal@cloudzero.com
import abc
from enum import Enum
from typing import Mapping, Any, List, NamedTuple

from czc.api.v2 import pagination
from czc.api.v2.constants import SORT_ORDER_ASC, SORT_ORDER_DESC
from czc.api.v2.pagination import PaginationSorting, PaginationArguments
from czc.api.web.filters import EMPTY_FILTERS, Filters


class SortKey(Enum):
    def sort_by(self, item: Mapping) -> Any:
        ...

    def available_values(self) -> List[str]:
        return [k.value for k in type(self)]


class SortOrder(Enum):
    asc = SORT_ORDER_ASC
    desc = SORT_ORDER_DESC


class CursorPage(NamedTuple):
    items: List[Mapping]
    cursor_response: dict


class Cursor(abc.ABC):
    sort_key: SortKey
    sort_order: SortOrder
    limit: int
    offset: int
    filters: Filters = EMPTY_FILTERS

    def paginate_items(self, items: List[Mapping]) -> CursorPage:
        sorting_arguments = PaginationSorting(self.sort_key.sort_by, self.sort_order.value)
        pagination_arguments = PaginationArguments(
            self.filters.pagination_filtering, sorting_arguments, self.offset, self.limit)
        response = pagination.paginate_items(items, pagination_arguments)
        cursors = pagination.limit_offset_cursors(self.serializable(), response.count_filtered_items)
        return CursorPage(items=response.items, cursor_response={
            **pagination.pagination_response(response.count_filtered_items, len(response.items), self.limit, *cursors),
            'sorting': {
                'available': {
                    'sort_keys': self.sort_key.available_values(),
                    'sort_orders': [o.value for o in SortOrder],
                },
                'current': [{'sort_key': self.sort_key.value, 'sort_order': self.sort_order.value}],
            },
            'filtering': self.filters.filtering_info,
            'total_count': response.count_all_items
        })

    @abc.abstractmethod
    def serializable(self) -> Mapping:
        ...
