from django.db import transaction
from rest_framework import status, filters
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

from apps.authentication.api.v1.api import UserViewSet
from apps.authentication.models import Organization
from apps.core.api import BaseViewSet
from apps.core.mixins.search_mixin import DynamicSearchMixin
from apps.core.mixins.soft_delete_mixin import SoftDeleteMixin
from apps.herd.models import Herd, Rancher, RancherOrganizationLink
from apps.herd.services.rancher_dashboard_service import RancherDashboardService
from apps.herd.services.rancher_org_link_services import RancherOrganizationService
from apps.herd.web.api.v1.serializers import HerdSerializer, RancherSerializer, RancherOrganizationLinkSerializer
from apps.livestock.web.api.v1.serializers import LiveStockSerializer
from apps.product.models import OrganizationQuotaStats
from apps.product.services.quota_dashboard_service import QuotaDashboardService
from apps.product.web.api.v1.serializers import product_serializers
from common.helpers import get_organization_by_user
from common.tools import CustomOperations


class HerdViewSet(BaseViewSet, SoftDeleteMixin, viewsets.ModelViewSet):
    """ Herd ViewSet """
    queryset = Herd.objects.all()
    serializer_class = HerdSerializer
    filter_backends = [filters.SearchFilter]
    search_fields = [
        "rancher__ranching_farm",
        "rancher__first_name",
        "rancher__last_name",
        "rancher__mobile",
        "rancher__national_code",
        "rancher__address",
        "rancher__province__name",
        "rancher__city__name",
        "name",
        "code",
        "province__name",
        "city__name",
        "postal",
        "institution",
        "epidemiologic",
        "activity",
        "capacity",
    ]

    def list(self, request, *args, **kwargs):
        """ list of herds """

        search = self.filter_queryset(self.get_queryset().order_by('-modify_date'))  # search & filter
        page = self.paginate_queryset(search)
        if page is not None:  # noqa
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

    @transaction.atomic
    def create(self, request, *args, **kwargs):
        """ create herd with user """
        organization = get_organization_by_user(request.user)

        if 'user' in request.data.keys():
            #  create user if owner of herd is not exist
            user = CustomOperations().custom_create(
                request=request,
                view=UserViewSet(),
                data_key='user'
            )
            owner = user['id']
            request.data.update({'owner': owner})

        # if cooperative do not send in request, set my organization in it
        if 'cooperative' not in request.data.keys():
            request.data.update({'cooperative': organization.id})

        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_403_FORBIDDEN)

    @action(
        methods=['get'],
        detail=False,
        url_name='my_herds',
        url_path='my_herds',
        name='my_herds'
    )
    @transaction.atomic
    def my_herds(self, request):
        """ get current user herds """
        serializer = self.serializer_class(self.get_queryset().filter(owner=request.user.id), many=True)
        if serializer.data:
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            return Response(status=status.HTTP_204_NO_CONTENT)

    @action(
        methods=['get'],
        detail=True,
        url_path='live_stocks',
        url_name='live_stocks',
        name='live_stocks'
    )
    def live_stocks(self, request, pk=None):
        """ list of herd live_stocks"""

        herd = self.get_object()
        queryset = herd.live_stock_herd.all()  # get herd live_stocks

        # paginate queryset
        page = self.paginate_queryset(queryset)
        if page is not None:  # noqa
            serializer = LiveStockSerializer(page, many=True)
            return self.get_paginated_response(serializer.data)


class RancherViewSet(BaseViewSet, RancherDashboardService, SoftDeleteMixin, viewsets.ModelViewSet, DynamicSearchMixin):
    queryset = Rancher.objects.all()
    serializer_class = RancherSerializer
    search_fields = [
        "ranching_farm",
        "first_name",
        "last_name",
        "mobile",
        "national_code",
        "birthdate",
        "nationality",
        "address",
        "province__name",
        "city__name",
    ]

    def list(self, request, *args, **kwargs):
        """ list of ranchers """

        search = self.filter_query(self.get_queryset().order_by('-modify_date'))  # search & filter
        page = self.paginate_queryset(search)
        if page is not None:  # noqa
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

    @action(
        methods=['get'],
        detail=True,
        url_path='herds',
        url_name='herds',
        name='herds'
    )
    def herds_by_rancher(self, request, pk=None):
        """ list of rancher herds """

        rancher = self.get_object()
        queryset = rancher.herd.all().order_by('-modify_date')  # get rancher herds

        # paginate queryset
        page = self.paginate_queryset(queryset)
        if page is not None:  # noqa
            serializer = HerdSerializer(page, many=True)
            return self.get_paginated_response(serializer.data)

    @action(
        methods=['get'],
        detail=True,
        url_path='rancher_plans',
        url_name='rancher_plans',
        name='rancher_plans'
    )
    def rancher_incentive_plans(self, request, pk=None):
        """ return list of rancher plans with information """

        rancher = self.get_object()  # rancher object

        page = self.paginate_queryset(rancher.plans.all())
        if page is not None:  # noqa
            serializer = product_serializers.IncentivePlanRancherSerializer(page, many=True)
            return self.get_paginated_response(serializer.data)

    @action(
        methods=['get'],
        detail=False,
        url_path='rancher_main_dashboard',
        url_name='rancher_main_dashboard',
        name='rancher_main_dashboard'
    )
    def rancher_main_dashboard(self, request, pk=None):
        """ rancher main dashboard """

        query_param = self.request.query_params  # noqa

        query_string = query_param.get('search', None)

        rancher_dashboard_data = self.get_main_dashboard(
            self,
            rancher_search_fields=self.search_fields,
            herd_search_fields=HerdViewSet.search_fields,
            query_string=query_string,
        )
        return Response(rancher_dashboard_data)

    @action(
        methods=['get'],
        detail=True,
        url_path='rancher_dashboard',
        url_name='rancher_dashboard',
        name='rancher_dashboard'
    )
    def rancher_dashboard(self, request, pk=None):
        """ rancher dashboard """

        rancher = self.get_object()

        query_param = self.request.query_params  # noqa

        query_string = query_param.get('search', None)

        rancher_dashboard_data = self.get_rancher_dashboard(
            self,
            rancher=rancher
        )
        return Response(rancher_dashboard_data)

    @action(
        methods=['get'],
        detail=True,
        url_path='rancher_dashboard_by_quota_usage',
        url_name='rancher_dashboard_by_quota_usage',
        name='rancher_dashboard_by_quota_usage'
    )
    def ranchers_dashboard_by_quota_usage(self, request, pk=None):
        """ rancher dashboard """

        rancher = self.get_object()

        query_param = self.request.query_params  # noqa

        query_string = query_param.get('search', None)

        rancher_dashboard_data = self.get_rancher_dashboard_by_quota_usage(
            self,
            rancher=rancher
        )
        return Response(rancher_dashboard_data)

    @action(
        methods=['get'],
        detail=True,
        url_path='rancher_dashboard_by_product_usage',
        url_name='rancher_dashboard_by_product_usage',
        name='rancher_dashboard_by_product_usage'
    )
    def rancher_dashboard_by_product_usage(self, request, pk=None):
        """ rancher dashboard """

        rancher = self.get_object()

        query_param = self.request.query_params  # noqa

        query_string = query_param.get('search', None)

        rancher_dashboard_data = self.get_rancher_dashboard_by_product_usage(
            self,
            rancher=rancher
        )
        return Response(rancher_dashboard_data)


class RancherOrganizationLinkViewSet(
    BaseViewSet,
    SoftDeleteMixin,
    viewsets.ModelViewSet,
    RancherOrganizationService,
    QuotaDashboardService,
    DynamicSearchMixin
):
    queryset = RancherOrganizationLink.objects.select_related('organization', 'rancher')
    serializer_class = RancherOrganizationLinkSerializer
    search_fields = [
        "rancher__ranching_farm",
        "rancher__first_name",
        "rancher__last_name",
        "rancher__mobile",
        "rancher__national_code",
        "rancher__birthdate",
        "rancher__nationality",
        "rancher__address",
        "rancher__province__name",
        "rancher__city__name",
        "organization__name"
    ]

    @action(
        methods=['get'],
        detail=False,
        name='org_linked_rancher_list',
        url_name='org_linked_rancher_list',
        url_path='org_linked_rancher_list',
    )
    def org_linked_rancher_list(self, request):
        """
        list of organizations with rancher information
        """
        query_params = self.request.query_params  # noqa
        if not 'org_id' in query_params.keys():
            org = get_organization_by_user(request.user)
        else:
            org = Organization.objects.get(id=query_params.get('org_id'))

        result = self.orgs_linked_rancher(org=org, org_type_key='CO')

        page = self.paginate_queryset(result)  # paginate queryset
        if page is not None:
            return self.get_paginated_response(page)
        return Response(result)

    @action(
        methods=['get'],
        detail=True,
        url_name='org_ranchers',
        url_path='org_ranchers',
        name='org_ranchers'
    )
    def org_ranchers(self, request, pk=None):
        """
        list of ranchers by organization
        """

        org_id = pk
        queryset = self.get_queryset().filter(organization_id=org_id)

        page = self.paginate_queryset(queryset)
        if page is not None:  # noqa
            serializer = self.serializer_class(page, many=True)
            return self.get_paginated_response(serializer.data)
        return Response(status=status.HTTP_200_OK)

    @action(
        methods=['get'],
        detail=True,
        url_name='org_ranchers_quota_dashboard',
        url_path='org_ranchers_quota_dashboard',
        name='org_ranchers_quota_dashboard'
    )
    def org_ranchers_quota_dashboard(self, request, pk=None):
        """
        dashboard of Org ranchers report by quota
        """
        org_obj = Organization.objects.get(id=pk)
        dashboard_data = self.get_dashboard(self, org=org_obj)

        return Response(dashboard_data)

    @action(
        methods=['get'],
        detail=True,
        url_name='org_ranchers_product_dashboard',
        url_path='org_ranchers_product_dashboard',
        name='org_ranchers_product_dashboard'
    )
    def org_ranchers_product_dashboard(self, request, pk=None):
        """
        dashboard of Org ranchers report by quota
        """
        org_obj = Organization.objects.get(id=pk)

        # get organization quota stats and get product ids
        org_quota_stat = OrganizationQuotaStats.objects.select_related('organization', 'quota').filter(
            organization=org_obj,
            quota__is_closed=False
        )

        products = {f'{stat.quota.product.name}': stat.quota.product.id for stat in org_quota_stat}

        dashboard_data = self.get_dashboard_by_product(self, organization=org_obj, products=products)

        return Response(dashboard_data)
