from __future__ import absolute_import, unicode_literals
import os
from threading import Thread
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
from celery import shared_task
import requests
from apps.am.models import Area
from apps.ecm.models import EventCate, Event
from apps.opm.models import Opl
from apps.third.dahua import dhClient
from apps.third.xunxi import xxClient
from apps.third.models import TDevice
from apps.third.tapis import xxapis
from django.utils import timezone
import time
from django.core.cache import cache
from django.conf import settings
from apps.utils.tasks import CustomTask
from apps.ecm.models import AlgoChannel
from datetime import timedelta
from apps.ai.main import algo_dict
from datetime import datetime
import uuid
from apps.ecm.serializers import EventSerializer
from django.utils import timezone
from django.core.exceptions import ObjectDoesNotExist

@shared_task(base=CustomTask)
def store_img(code: str, duration: int):
    while True:
        global_img = dhClient.snap(code)
        file_name = global_img.split('/')[-1].split('?')[0]
        res = requests.get(url=global_img, verify=False)
        path = '/media/temp/store_img/'
        full_path = settings.BASE_DIR + path
        if not os.path.exists(full_path):
            os.makedirs(full_path)
        with open(full_path + file_name, 'wb') as f:
            f.write(res.content)
        time.sleep(duration)


def update_count_people(i: Area):
    from apps.ecm.service import handle_xx_event_3
    if i.third_info.get('xx_rail', None):
        railId = i.third_info['xx_rail']['id']
        json = {"railId": railId, "type": ""}
        _, res = xxClient.request(**xxapis['rail_ibeacon_list'], json=json)
        blt_list = res['recordList']
        macs = []
        for m in blt_list:
            macs.append(m['userId'])
        count_people = TDevice.objects.filter(
            type=TDevice.DEVICE_BLT, obj_cate='people', code__in=macs).exclude(employee=None).count()
        i.count_people = count_people
        i.save()
        if count_people >= i.count_people_max:
            # 触发超员事件
            handle_xx_event_3('over_man', i)
        elif count_people < i.count_people_min:
            # 触发缺员事件
            handle_xx_event_3('lack_man', i)
        return {'count': count_people}


@shared_task
def cal_area_count():
    """
    计算区域内人员数量
    """
    areas = Area.objects.filter(type=Area.AREA_TYPE_FIX)
    for i in areas:
        update_count_people(i)


@shared_task
def check_event_timeout():
    """判断事件处理是否超期
    """
    for i in Event.objects.filter(handle_user=None, is_timeout=False):
        cate = i.cates.all().order_by('priority', 'create_time').first()
        if cate and cate.handle_minute > 0 and (timezone.now()-i.create_time).seconds > cate.handle_minute * 60:
            i.is_timeout = True
            i.save()


@shared_task
def opl_task(vc_codes: list, opl_id: str):
    """作业监控任务(废弃)
    """
    from apps.ecm.service import snap_and_analyse
    start_time = time.time()
    opl_cate = Opl.objects.get(id=opl_id).cate
    # 找到作业需要加载的算法
    algo_codes = list(EventCate.objects.filter(opl_cates=opl_cate).values_list('code', flat=True))
    vchannels = TDevice.objects.filter(code__in=vc_codes)
    opl = Opl.objects.get(id=opl_id)
    while time.time()-start_time < 10800:   # 一次任务不手动关闭最多持续3小时
        for i in vchannels:
            Thread(target=snap_and_analyse, args=(i, algo_codes, opl), daemon=True).start()
        time.sleep(10)
    opl.mtask_uid = None
    opl.save()


@shared_task(base=CustomTask)
def monitor_check(vc_ids = []):
    """

    监控任务监控重启/可每隔一段时间执行一次
    """
    if vc_ids:
        vcs = TDevice.objects.filter(id__in=vc_ids, type=TDevice.DEVICE_VCHANNEL)
    else:
        vcs = TDevice.objects.filter(ac_vchannel__always_on=True, type=TDevice.DEVICE_VCHANNEL)
    for vc in vcs:   # 获取每一个要开启的视频通道
        algo_codes = AlgoChannel.objects.filter(vchannel=vc, always_on=True).values_list('algo__code', flat=True)
        ckey = 'vchannel_' + vc.id
        if algo_codes:
            tid = str(uuid.uuid4())
            cache.set(ckey, {'id': tid, 'algo_codes': algo_codes, 'start_time':  datetime.now().strftime("%Y-%m-%d %H:%M:%S")}, timeout=60)
            Thread(target=loop_and_analyse, args=(vc, tid), daemon=True).start()
        else:
            cache.delete(ckey)

@shared_task(base=CustomTask)
def loop_and_analyse(vc: TDevice, tid: str):
    from apps.ecm.service import snap_and_analyse
    ckey = 'vchannel_' + vc.id
    while cache.get(ckey, {}).get('id', None) == tid:
        vc_dict = cache.get(ckey, {})
        if 'algo_codes' in vc_dict:
            intersection = list(set(vc_dict['algo_codes']) & set(algo_dict.keys()))   # 取布设与算法的交集
            if intersection:
                Thread(target=snap_and_analyse, args=(vc, intersection), daemon=True).start()
        time.sleep(10)

@shared_task(base=CustomTask)
def monitor_and_analyse(vchannel_code: str, algo_codes: list):
    """rtsp流监控进行算法识别

    Args:
        vchannel_code (str): 视频通道编号
        algo_codes (list): 算法代号
    """
    # cates = EventCate.objects.filter(loop_on=True, self_algo=True)
    # AlgoChannel.objects.filter(algo__loop_on=True, algo__self_algo=True).values('algo__code', 'vchannel')
    import cv2
    # RTSP URL
    rtsp_url = dhClient.get_rtsp(vchannel_code)
    # 打开RTSP流
    cap = cv2.VideoCapture(rtsp_url)
    # 检查是否成功打开
    if not cap.isOpened():
        print("Error opening video stream or file")

    # 循环读取帧
    while cap.isOpened():
        ret, frame = cap.read()

        if not ret:
            break

        # 在这里可以对每一帧进行处理

        # 显示帧
        cv2.namedWindow("Frame", 0)
        cv2.resizeWindow("Frame", 1600, 900)
        cv2.imshow("Frame", frame)

        # 按 'q' 键退出
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    # 释放资源
    cap.release()
    cv2.destroyAllWindows()

@shared_task(base=CustomTask)
def compressed_all_ecm_image():
    from apps.ecm.service import compress_global_img
    events = Event.objects.exclude(global_img=None)
    for event in events:
        if event.global_img and event.global_img_compressed is None:
            event.global_img_compressed = compress_global_img(event.global_img)
            event.save()

@shared_task(base=CustomTask)
def remind_push(remindId: str):
    from apps.ecm.models import Remind
    from apps.ecm.serializers import RemindSerializer
    remind = Remind.objects.get(id=remindId)

    channel_layer = get_channel_layer()
    data = {
        'type': 'remind',
        'remind': RemindSerializer(instance=remind).data,
        'msg': ''
    }
    async_to_sync(channel_layer.group_send)(f"user_{remind.recipient.id}", data)


@shared_task(base=CustomTask)
def event_push(eventId: str):
    try:
        event = Event.objects.get(id=eventId)
    except ObjectDoesNotExist:
        return
    if event.handle_time is None:
        now = timezone.now()
        channel_layer = get_channel_layer()
        data = {
            'type': 'event',
            'event': EventSerializer(instance=event).data,
            'msg': ''
        }
        async_to_sync(channel_layer.group_send)('event', data)
        event.last_ws_pushtime = timezone.now()
        event.save()
        if event.create_time + timedelta(hours=48) < now:
            pass
        else:
            main_cate = event.cates.all().order_by('priority', 'create_time').first()
            unhandled_push_interval = main_cate.unhandled_push_interval
            event_push.apply_async(args=[eventId], eta=now + timedelta(seconds=unhandled_push_interval))