from django.shortcuts import render
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from django.utils import timezone
from apps.em.models import Equipment, EcheckRecord, EInspect, Ecate, Repair
from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
from apps.em.serializers import (EquipmentSerializer, EcheckRecordSerializer, 
EInspectSerializer, EcateSerializer, CdSerializer, RepairCreateSerializer, RepairListSerializer)
from apps.em.filters import EquipFilterSet
from rest_framework.exceptions import ParseError
from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin
from dateutil.relativedelta import relativedelta
from rest_framework.decorators import action
from rest_framework.serializers import Serializer
from django.db import transaction
from apps.em.services import daoru_equipment
from apps.em import cd
from rest_framework.response import Response
from django.conf import settings
from django.db.models import Count, Case, When, IntegerField
from apps.enp.services import get_last_envdata
from rest_framework.views import APIView
from apps.utils.mixins import MyLoggingMixin
import importlib
from apps.wf.mixins import TicketMixin
from apps.wf.models import Ticket
from apps.system.models import User

# Create your views here.


class EcateViewSet(CustomModelViewSet):
    """
    list:设备分类

    设备分类
    """

    queryset = Ecate.objects.all()
    serializer_class = EcateSerializer
    ordering = ["id", "type", "code", "create_time"]
    filterset_fields = {"type": ["exact", "in"], "code": ["exact", "in", "contains"], "is_for_safe": ["exact"], "is_for_enp": ["exact"], "is_car": ["exact"]}


class EquipmentViewSet(CustomModelViewSet):
    """
    list:设备列表

    设备列表
    """

    queryset = Equipment.objects.get_queryset(all=True)
    serializer_class = EquipmentSerializer
    select_related_fields = ["create_by", "belong_dept", "keeper", "mgroup"]
    search_fields = ["number", "name"]
    filterset_class = EquipFilterSet

    def get_queryset(self):
        if self.request.method == 'GET' and (not self.request.query_params.get('is_deleted', None)):
            self.queryset = Equipment.objects.all()
        return super().get_queryset()

    @action(methods=["post"], detail=False, perms_map={"post": "equipment.create"}, serializer_class=Serializer)
    @transaction.atomic
    def daoru(self, request, *args, **kwargs):
        """导入

        导入
        """
        daoru_equipment(settings.BASE_DIR + request.data.get("path", ""))
        return Response()

    @swagger_auto_schema(
        manual_parameters=[
            openapi.Parameter(name="has_envdata", in_=openapi.IN_QUERY, description="Include envdata in the response", type=openapi.TYPE_STRING, enum=["yes", "no"], required=False),
            openapi.Parameter(name="query", in_=openapi.IN_QUERY, description="定制返回数据", type=openapi.TYPE_STRING, required=False),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

    def add_info_for_list(self, data):
        if self.request.query_params.get("has_envdata", "no") == "yes":
            now = timezone.localtime()
            now_10_before = now - timezone.timedelta(minutes=10)
            edata_dict = {}
            if data:
                data_ids = [item["id"] for item in data]
                edata = get_last_envdata(data_ids, now_10_before, now)
                edata_dict = {item["equipment"]: item for item in edata}
            for item in data:
                item["envdata"] = edata_dict.get(item["id"], {})
        return data

    @action(methods=["get"], detail=False, perms_map={"get": "*"})
    def count_running_state(self, request, *args, **kwargs):
        """当前运行状态统计

        当前运行状态统计
        """
        queryset = self.filter_queryset(self.get_queryset())
        result = queryset.aggregate(
            count=Count("id"),
            count_running=Count(Case(When(running_state=10, then=1), output_field=IntegerField())),
            count_standby=Count(Case(When(running_state=20, then=1), output_field=IntegerField())),
            count_stop=Count(Case(When(running_state=30, then=1), output_field=IntegerField())),
            count_fail=Count(Case(When(running_state=40, then=1), output_field=IntegerField())),
            count_offline=Count(Case(When(running_state=50, then=1), output_field=IntegerField())),
            count_ok=Count(Case(When(state=Equipment.EQUIP_STATE_OK, then=1), output_field=IntegerField())),
            count_limit=Count(Case(When(state=Equipment.EQUIP_STATE_LIMIT, then=1), output_field=IntegerField())),
            count_fix=Count(Case(When(state=Equipment.EQUIP_STATE_FIX, then=1), output_field=IntegerField())),
            count_disable=Count(Case(When(state=Equipment.EQUIP_STATE_DISABLE, then=1), output_field=IntegerField())),
            count_scrap=Count(Case(When(state=Equipment.EQUIP_STATE_SCRAP, then=1), output_field=IntegerField())),
        )
        json_result = {
            "count": result["count"],
            "count_running": result["count_running"],
            "count_standby": result["count_standby"],
            "count_stop": result["count_stop"],
            "count_fail": result["count_fail"],
            "count_offline": result["count_offline"],
            "count_ok": result["count_ok"],
            "count_limit": result["count_limit"],
            "count_fix": result["count_fix"],
            "count_disable": result["count_disable"],
            "count_scrap": result["count_scrap"]
        }

        return Response(json_result)

    @swagger_auto_schema(
        manual_parameters=[
            openapi.Parameter(name="host", in_=openapi.IN_QUERY, description="SVS IP", type=openapi.TYPE_STRING, required=True),
            openapi.Parameter(name="port", in_=openapi.IN_QUERY, description="SVS 端口", type=openapi.TYPE_STRING, required=True),
            openapi.Parameter(name="flow_unit", in_=openapi.IN_QUERY, description="识别码(流程单元号), 多台 SVS 共用 IP 时用于区分设备", type=openapi.TYPE_STRING, required=True),
        ]
    )
    @action(methods=["get"], detail=False, perms_map={"get": "*"})
    def get_svs_char(self, request, *args, **kwargs):
        """获取 SVS 识别字符

        触发 SVS 拍照并取回识别字符, flow_unit 为识别码用于区分共用 IP 的不同设备
        """
        host = request.query_params.get("host", None)
        port = request.query_params.get("port", None)
        if not host or not port:
            raise ParseError("请传入host和port参数")
        flow_unit = request.query_params.get("flow_unit", 0)
        return Response(cd.get_svs_char(host, port, flow_unit))


class EcheckRecordViewSet(ListModelMixin, CreateModelMixin, DestroyModelMixin, CustomGenericViewSet):
    """
    list:校准/检定记录

    校准/检定记录
    """

    perms_map = {"get": "*", "post": "echeckrecord.create", "put": "echeckrecord.update", "delete": "echeckrecord.delete"}
    queryset = EcheckRecord.objects.all()
    serializer_class = EcheckRecordSerializer
    select_related_fields = ["equipment", "create_by"]
    filterset_fields = ["equipment", "result"]

    def perform_create(self, serializer):
        instance = serializer.save()
        equipment = instance.equipment
        if equipment.cycle:
            equipment.check_date = instance.check_date
            equipment.next_check_date = instance.check_date + relativedelta(months=equipment.cycle)
            equipment.save()

    def perform_destroy(self, instance):
        instance.delete()
        equipment = instance.equipment
        er = EcheckRecord.objects.filter(equipment=equipment).order_by("check_date").last()
        if er:
            equipment.check_date = instance.check_date
            equipment.next_check_date = instance.check_date + relativedelta(months=equipment.cycle)
            equipment.save()
        else:
            equipment.check_date = None
            equipment.next_check_date = None
            equipment.save()


class EInspectViewSet(CustomModelViewSet):
    """
    list:巡检记录

    巡检记录
    """

    queryset = EInspect.objects.all()
    serializer_class = EInspectSerializer
    select_related_fields = ["equipment", "inspect_user"]
    filterset_fields = ["equipment"]


class CdView(MyLoggingMixin, APIView):

    @swagger_auto_schema(request_body=CdSerializer)
    def post(self, request, format=None):
        """执行采集数据方法

        执行采集数据方法
        """
        method = request.data.get("method", None)
        if not method:
            raise ParseError("请传入method参数")
        m = method.split("(")[0]
        args = method.split("(")[1].split(")")[0].split(",")
        module, func = m.rsplit(".", 1)
        m = importlib.import_module(module)
        f = getattr(m, func)
        return Response(f(*args))


class RepairViewSet(TicketMixin, CustomModelViewSet):
    """
    list:维修申请

    维修申请
    """
    queryset = Repair.objects.all()
    serializer_class = RepairCreateSerializer
    list_serializer_class = RepairListSerializer
    retrieve_serializer_class = RepairListSerializer
    select_related_fields = ["equipment", "repair_user", "create_by"]
    filterset_fields = ["equipment", "repair_user", "ticket__state__type"]
    search_fields = ["equipment__name", "equipment__number", "fault_description", "fault_cate", "repair_description"]
    workflow_key = "wf_repair"
    
    def gen_other_ticket_data(self, instance):
        return {"equipment_fullname": str(instance.equipment)}

    @staticmethod
    def assgin(ticket: Ticket, transition, new_ticket_data: dict):
        repair_user = new_ticket_data.get("repair_user", None)
        fault_cate = new_ticket_data.get("fault_cate", None)
        if repair_user and fault_cate:
            repair = Repair.objects.get(ticket=ticket)
            repair.repair_user = User.objects.get(id=repair_user)
            repair.fault_cate = fault_cate
            repair.save()
        else:
            raise ParseError("请分派维修人")
    
    @staticmethod
    def repair_completed(ticket: Ticket, transition, new_ticket_data: dict):
        repair_start_time = new_ticket_data.get("repair_start_time", None)
        repair_duration = new_ticket_data.get("repair_duration", None)
        repair_description = new_ticket_data.get("repair_description", None)
        if repair_start_time and repair_duration:
            repair = Repair.objects.get(ticket=ticket)
            repair.repair_start_time = repair_start_time
            repair.repair_duration = repair_duration
            repair.repair_description = repair_description
            repair.save()
        else:
            raise ParseError("请填写维修开始时间和维修工时")