from abc import abstractmethod

from typing import List, Dict


class BaseServiceClass:
    def __init__(self, model, serializer_class, **kwargs):
        self.model = model
        self.serializer_class = serializer_class

    def get_model(self):
        if self.model is None:
            raise Exception('The model field cannot be empty. This field specifies the model class in question.')
        return self.model

    def get_serializer_class(self):
        if self.serializer_class is None:
            raise Exception('The model field cannot be empty. This field specifies the serializer class in question.')
        return self.serializer_class

    def get_queryset(self):
        return self.get_model().objects.all()

    def get_serializer(self, **kwargs):
        return self.get_serializer_class()(**kwargs)

    @abstractmethod
    def execute(self, *args, **kwargs):
        pass


class BaseGetServiceClass(BaseServiceClass):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.filterset_fields = kwargs.get('filterset_fields', [])
        self.distinct = kwargs.get('distinct', False)

    def filtered(self, qs, *args, **kwargs):
        filters = {}
        for k, v in kwargs.items():
            if k in self.filterset_fields:
                filters[k] = v
        if not len(filters):
            return qs
        ret = qs.filter(**filters).all()
        return ret

    def distincter(self, qs, *args, **kwargs):
        if not self.distinct:
            return qs
        ret = qs.distinct()
        return ret

    def get_queryset(self):
        return self.get_model().objects.all()

    @abstractmethod
    def execute(self, *args, **kwargs):
        pass


class CreateService(BaseServiceClass):
    def create(self, data: dict, *args, **kwargs):
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)

        instance = self.get_model().objects.create(**serializer.validated_data)

        ret = self.get_serializer(instance=instance).data
        return ret

    def execute(self, *args, **kwargs):
        return self.create(*args, **kwargs)


class BulkCreateService(BaseServiceClass):
    def bulk_create(self, data: List[Dict], *args, **kwargs):
        objs = []
        for i in data:
            serializer = self.get_serializer(data=i)
            serializer.is_valid(raise_exception=True)

            objs.append(self.model(**serializer.validated_data))

        if not len(objs):
            return []
        instances = self.model.bulk_create(*objs)
        ret = self.get_serializer(instance=instances, many=True).data
        return ret

    def execute(self, *args, **kwargs):
        return self.bulk_create(*args, **kwargs)


class ListService(BaseGetServiceClass):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.search_fields = kwargs.get('search_fields', [])
        self.ordering_fields = kwargs.get('ordering_fields', [])

        self.paginate = kwargs.get('paginate', False)

    def get_qs_count(self, qs, *args, **kwargs):
        ret = qs.count()
        return ret

    def get_page_size(self, *args, **kwargs):
        ret = kwargs.get('page_size', 100)
        return ret

    def get_page(self, *args, **kwargs):
        ret = kwargs.get('page', 1)
        return ret

    def search(self, qs, *args, **kwargs):
        return qs

    def ordering(self, qs, *args, **kwargs):
        order_by = []
        for i in kwargs.get('order_by', []):
            if i.replace('-', '', 1) in self.ordering_fields:
                order_by.append(i)
        if not len(order_by):
            return qs
        ret = qs.order_by(*order_by).all()
        return ret

    def pagination(self, qs, *args, **kwargs):
        if not self.paginate:
            return qs

        page_size = self.get_page_size(*args, **kwargs)
        page = self.get_page(*args, **kwargs)

        ret = qs[page_size * (page - 1):page_size * page]
        return ret

    def return_paginate_result(self, data, qs, *args, **kwargs):
        if not self.paginate:
            return data

        ret = {
            'count': self.get_qs_count(qs, *args, **kwargs),
            'page_size': self.get_page_size(*args, **kwargs),
            'page': self.get_page(*args, **kwargs),
            'data': data,
        }
        return ret

    def get_list(self, *args, **kwargs):
        qs = self.get_queryset()
        qs = self.filtered(qs, *args, **kwargs)
        qs = self.search(qs, *args, **kwargs)
        qs = self.distincter(qs, *args, **kwargs)
        qs = self.ordering(qs, *args, **kwargs)
        instances = self.pagination(qs, *args, **kwargs)
        data = self.get_serializer(instance=instances, many=True).data
        ret = self.return_paginate_result(data, qs, *args, **kwargs)
        return ret

    def execute(self, *args, **kwargs):
        return self.get_list(*args, **kwargs)


class RetrieveService(BaseGetServiceClass):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.lockup_field = kwargs.get('lockup_field', 'id')

    def retrieve(self, *args, **kwargs):
        qs = self.get_queryset()
        qs = self.filtered(qs, *args, **kwargs)
        instance = qs.filter(**{self.lockup_field: kwargs.get(self.lockup_field)}).first()
        ret = self.get_serializer(instance=instance).data
        return ret

    def execute(self, *args, **kwargs):
        return self.retrieve(*args, **kwargs)


class UpdateService(BaseGetServiceClass):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.lockup_field = kwargs.get('lockup_field', 'id')

    def update(self, data: dict, *args, **kwargs):
        qs = self.get_queryset()
        qs = self.filtered(qs, *args, **kwargs)

        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)

        instance = qs.filter(**{self.lockup_field: kwargs.get(self.lockup_field)}).update(**serializer.validated_data)
        ret = self.get_serializer(instance=instance).data
        return ret

    def execute(self, *args, **kwargs):
        return self.update(*args, **kwargs)


class BulkUpdateService(BaseGetServiceClass):

    def bulk_update(self, data: dict, *args, **kwargs):
        qs = self.get_queryset()
        qs = self.filtered(qs, *args, **kwargs)

        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)

        instance = qs.update(**serializer.validated_data)
        ret = self.get_serializer(instance=instance).data
        return ret

    def execute(self, *args, **kwargs):
        return self.bulk_update(*args, **kwargs)


class DestroyService(BaseGetServiceClass):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.lockup_field = kwargs.get('lockup_field', 'id')

    def destroy(self, *args, **kwargs):
        qs = self.get_queryset()
        qs = self.filtered(qs, *args, **kwargs)
        qs.filter(**{self.lockup_field: kwargs.get(self.lockup_field)}).delete()
        return

    def execute(self, *args, **kwargs):
        return self.destroy(*args, **kwargs)

    def get_serializer_class(self):
        return

    def get_serializer(self, **kwargs):
        return


class BulkDestroyService(BaseGetServiceClass):

    def bulk_destroy(self, data: dict, *args, **kwargs):
        qs = self.get_queryset()
        qs = self.filtered(qs, *args, **kwargs)

        qs.delete()
        return

    def execute(self, *args, **kwargs):
        return self.bulk_destroy(*args, **kwargs)

    def get_serializer_class(self):
        return

    def get_serializer(self, **kwargs):
        return
