import re
import requests
import time
import ast
import os 
import datetime
import uuid
from PIL import Image
from io import BytesIO
from django.conf import settings
from django.db.models import Count
from django.core.paginator import Paginator
from django.db import transaction

from smartfarming.api.views.forecast.all_dict import insect_dict, attract_discern
from smartfarming.models.sim_card import MongoMsg_Conf
from smartfarming.models.device import MongoDevice
from smartfarming.models.camera import MongoCameraPhoto, MongoCameraData, MongoCameraAccount
from smartfarming.serializers.device_serializers import DeviceSerializers
from smartfarming.utils import bindDeviceLive, getLive, controlMove, modify_live_time, off_on_live, setDeviceSnap, getKitToken, getRecord
from kedong.tools import RedisPool
from kedong.decoration import kedong_deco, PortError

from smartfarming.api.views.camera.cameraserializers import (
    MongoCameraPhotoSerializers,
    MongoDeviceCameraSerializers
)


config_dict = settings.CONFIG


@kedong_deco(login_required=True)
def list_camera(request):
    # 获取监控设备列表
    post_info = request.POST
    device_id = post_info.get('device_id')
    page_num = int(post_info.get('page', 1))
    page_size = int(post_info.get('page_size', 10))
    queryset = MongoDevice.objects.filter(device_type_id=6).order_by("-uptime")
    if device_id:
        queryset = queryset.filter(device_id=device_id)
    paginator = Paginator(queryset, page_size)
    page_obj = paginator.get_page(page_num)
    serializers = MongoDeviceCameraSerializers(page_obj, many=True)
    datas = {"data": serializers.data, "counts": queryset.count()}
    return datas


@kedong_deco(login_required=True)
def addr_camera_list(request):
    """
    获取监控设备播放地址 
    参数:
    device_id           必传  (string)                  监控设备的序列号

    返回值:
    {
    "data": {
            "hls": "http://test.ys7.com:66/openlive/b475942b03544a5aa35b651670d8712c.m3u8",                  HLS流畅直播地址
            "hlsHd": "http://test.ys7.com:66/openlive/b475942b03544a5aa35b651670d8712c.hd.m3u8",             HLS高清直播地址
            "rtmp": "rtmp://10.82.0.190:1935/openlive/b475942b03544a5aa35b651670d8712c",                     RTMP流畅直播地址
            "rtmpHd": "rtmp://10.82.0.190:1935/openlive/b475942b03544a5aa35b651670d8712c.hd"                 RTMP高清直播地址
            }
    "errorCode": 0,
    "message": "",
    "formError": {}
    }

    """
    try:
        if 'HTTP_X_FORWARDED_FOR' in request.META:
            ip = request.META['HTTP_X_FORWARDED_FOR']
        else:
            ip = request.META['HTTP_X_REAL_IP']
    except Exception as KeyError:
        ip = request.META['REMOTE_ADDR']
    device_id = request.POST.get('device_id')
    try:
        addr = MongoCameraData.objects.get(device_id=device_id).device_info
    except:
        addr = {}
    return addr


@kedong_deco(login_required=True)
def ctrl_camera(request):
    """
    控制监控设备转动 :
    参数:
    device_id          必传  (string)                  监控设备的序列号
    ctrl               必传  (string)                  操作指令  move 控制球机转动   stop 停止球机转动
    movenum            ctrl=move时必传(string)         0-上,1-下,2-左,3-右,8-放大,9-缩小

    返回值:
    {
    "data": success,     success成功   default失败
    "errorCode": 0,
    "message": "",
    "formError": {}
    }

    """
    device_id = request.POST.get('device_id')
    ctrl = request.POST.get('ctrl')
    account_id = MongoCameraData.objects.filter(device_id=device_id).first()
    if not account_id:
        raise PortError("","未找到此设备")
    camera_query = MongoCameraAccount.objects.filter(id=int(account_id.account_id)).first()
    if not camera_query:
        raise PortError("","未找到此账号")
    channelNo = "1"
    if device_id.find("-") != -1:
        channelNo = device_id.split("-")[1]
        device_id = device_id.split("-")[0]
    # 操作命令:0-上,1-下,2-左,3-右,4-左上,5-左下,6-右上,7-右下,8-放大,9-缩小,10-近焦距,11-远焦距
    if ctrl == "move":
        dire = request.POST.get('movenum')
        # 增加大华相机操作功能
        if camera_query.account_type == 1:
            if dire not in ("0","1","2","3","8","9"):
                raise PortError("movenum","暂不支持其它指令")
            appId = camera_query.app_key
            appSecret = camera_query.app_secret
            result = controlMove(appId,appSecret,device_id,dire,channelNo)
            code = result.get("result").get("code")
            if code == "0":
                return True 
            else:
                return {"errorCode": 2, "msg": result.get("result").get("msg")}
        else:
            accessToken = camera_query.token
            ctrl_url = 'https://open.ys7.com/api/lapp/device/ptz/start'
            speed = '1'
            data = {'accessToken':accessToken, 'deviceSerial':device_id, 'channelNo':channelNo, 'speed':speed, 'direction':dire}
            res1 = requests.post(ctrl_url,data=data)
    elif ctrl == "stop":
        time.sleep(0.4)
        # 增加大华相机操作功能
        if camera_query.account_type == 1:
            dire = "10"
            appId = camera_query.app_key
            appSecret = camera_query.app_secret
            result = controlMove(appId,appSecret,device_id,dire,channelNo)
            code = result.get("result").get("code")
            if code == "0":
                return True 
            else:
                return {"errorCode": 2, "msg": result.get("result").get("msg")}
        else:
            accessToken = camera_query.token
            ctrl_url = 'https://open.ys7.com/api/lapp/device/ptz/stop'
            data = {'accessToken':accessToken, 'deviceSerial':device_id, 'channelNo':channelNo}
            res2 = requests.post(ctrl_url,data=data)
    return True


@kedong_deco(login_required=False)
def see_camera_account(request):
    """
    查看当前绑定的萤石云账号信息\按业务员名字搜索\萤石云账号搜索
    
    参数：
    account          非必传(str)          按萤石云账号名字搜索
    salesman         非必传(str)          按业务员名字搜索
    
    说明：两个参数都不传为所有数据，不进行筛选操作

    返回值：
        {
        "errorCode": 0,
        "message": "",
        "formError": {},
        "data": [
            {
                "id": 1,                        萤石云账号id
                "account": "hnyf"               萤石云名称
            }
        ],
        "params": {}
    }
    """
    account = request.POST.get("account")
    salesman = request.POST.get("salesman")
    camera_query = MongoCameraAccount.objects.all().order_by("-id")
    if account:
        camera_query = camera_query.filter(account__icontains=account)
    if salesman:
        camera_query = camera_query.filter(salesman__icontains=salesman)
    data = [{'id':i.id,'account':i.account} for i in camera_query]
    return data



@kedong_deco(login_required=True)
def add_camera_account(request):
    """
    添加萤石云账号
    管理员可操作

    参数：
    account                     必传(str)               萤石云账号
    salesman                    必传(str)               萤石云账号所相关的业务员名称
    app_token                   必传(str)               萤石云账号 token 
    app_key                     必传(str)               萤石云账号 appkey
    app_secret                  必传(str)               萤石云账号 appsecret
    
    2022-08-19 08:54 csx 
    增加监控账户区分,在不影响原来的功能上进行修改,增加大华乐橙云账号添加
    account_type                非必传(number)          监控设备类型 0代表海康   1代表大华,默认是0
    
    
    """
    account = request.POST.get("account")
    salesman = request.POST.get("salesman")
    token = request.POST.get("app_token")
    app_key = request.POST.get("app_key")
    app_secret = request.POST.get("app_secret")
    account_type = request.POST.get("account_type","0")
    
    
    if account_type not in ("0","1"):
        raise PortError("","无法识别此监控账号类型")

    if not all([account,salesman,token,app_key,app_secret]):
        raise PortError("","请把参数填写完整")

    now_time = int(time.time())
    camera_query = MongoCameraAccount.objects.filter(app_key=app_key,account_type=account_type).first()
    if camera_query:
        raise PortError("","此appkey已存在,请勿重复添加")
    
    try:
        MongoCameraAccount.objects.create(account=account,salesman=salesman,token=token,addtime=now_time,uptime=now_time,
                                    app_key=app_key,app_secret=app_secret,account_type=account_type)
    except:
        raise PortError("","添加萤石账号失败")
    
    return True



@transaction.atomic
@kedong_deco(login_required=True)
def add_camera(request):
    """
    添加监控设备接口:
    参数：
    device_id          必传  (string)                  监控设备的序列号
    camera_channel     必传  (string)                  监控通道号
    camera_playback    必传  (string)                  是否支持回放功能,0不支持 1支持

    新加参数
    account_id         必传  (string)                  萤石云账号id
    
    
    2022-08-19 08:54 csx 
    增加监控账户区分,在不影响原来的功能上进行修改,增加大华乐橙云监控绑定

    返回值：
    {
    "data": null,
    "errorCode": 0,
    "message": ,
    "formError": {}
    }

    """
    device_id = request.POST.get('device_id')
    camera_channel = request.POST.get("camera_channel","0")
    camera_playback = request.POST.get("camera_playback","0")
    account_id = request.POST.get("account_id",1)
    
    if not account_id.isdigit():
        raise PortError("account_id","无效参数")
    
    if not device_id:
        raise PortError("device_id","未传设备号")
    
    if not camera_channel.isdigit():
        raise PortError("camera_channel","无效参数")
    
    
    if camera_playback not in ("0","1"):
        raise PortError("camera_playback","参数超出范围")
    
    now_time = int(time.time())
    camera_query = MongoCameraAccount.objects.filter(id=int(account_id)).first()
    if not camera_query:
        raise PortError("","未找到此账号")
    
    
    device_ids = device_id + "-" + camera_channel
    # if MongoCameraData.objects.filter(device_id=device_id).exists() or MongoDevice.objects.filter(device_id=device_id).exists():
    #     raise PortError("","设备已存在，请勿重复添加")
    
    # 使用的是大华乐橙云账号监控接入,监控通道号大华的从0开始
    coverUrl = ""
    if camera_query.account_type == 1:
        appId = camera_query.app_key
        appSecret = camera_query.app_secret

        device_live_data = bindDeviceLive(appId, appSecret, device_id,camera_channel)
        code = device_live_data["result"]["code"]
        
        if code == "0":
            live_path = device_live_data["result"]["data"]["streams"][0]["hls"]
        
        elif code == "LV1001":
            # 如直播地址已存在，则需要查询直播地址列表
            live_data = getLive(appId, appSecret, device_id, camera_channel)
            coverUrl = live_data["result"]["data"]["streams"][0]["coverUrl"]
            if live_data["result"]["code"] == "0":
                try:
                    live_path = live_data["result"]["data"]["streams"][0]["hls"]
                except:
                    raise PortError("","获取直播地址失败")
            else:
                raise PortError("",live_data)
        else:
            msg = device_live_data["result"]["msg"]
            raise PortError("", msg)        
        device_info = {
                "hls": live_path,
                "hlsHd": live_path,
                "rtmp": live_path,
                "rtmpHd": live_path
                }
    else:
        accessToken = camera_query.token
        url = config_dict.get("camera").get("ys_open")
        data = {"accessToken":accessToken,"source":"%s:%s"%(device_id,str(camera_channel))}
        res = requests.post(url,data=data)
        if ast.literal_eval(res.text)["code"] == "200":
            url_1 = config_dict.get("camera").get("ys_get")
            res_1 = requests.post(url_1,data=data)
            try:
                addr = ast.literal_eval(res_1.text)["data"][0]
                device_info = {
                    "hls": addr["hls"],
                    "hlsHd": addr["hlsHd"],
                    "rtmp": addr["rtmp"],
                    "rtmpHd": addr["rtmpHd"]
                    }
            except Exception as e:
                raise PortError("device_id", "解析结果异常")
        else:
            raise PortError("","Token过期,设备激活失败")
    try:
        MongoDevice.objects.create(device_id=device_ids,device_type_id=6,addtime=now_time,device_code=1,uptime=now_time, jk_live_img=coverUrl)
        MongoCameraData.objects.create(device_id=device_ids,device_info=device_info,addtime=now_time,camera_playback=camera_playback,account_id=account_id)
    except Exception as e:
        print(e)
        raise PortError(" ", "添加设备失败")
    return True



@kedong_deco(login_required=True)
def camera_config(request):
    """
    监控设置
    参数:
    device_name                 必传(str)               设备名称
    off_on                      必传(str)               设备开关 off 关 on 开
    start_time                  非必传(str)             开始时间 为开时 必传
    end_time                    非必传(str)             结束时间 为开时 必传
    device_id                   必传(str)               设备号 
    """
    
    device_id = request.POST.get("device_id") 
    off_on = request.POST.get("off_on","")
    start_time = request.POST.get("start_time","")
    end_time = request.POST.get("end_time","")
    device_ids = request.POST.get("device_id")
    device_name = request.POST.get("device_name")
    redis_pool = RedisPool().get_redis_pool(settings.redis_db["captcha"])
    try:
        device_query = MongoDevice.objects.get(device_id=device_ids)
    except:
        raise PortError("","未找出此设备")
    account_id = MongoCameraData.objects.filter(device_id=device_ids).first()
    if not account_id:
        raise PortError("","未找到此设备")
    camera_query = MongoCameraAccount.objects.filter(id=int(account_id.account_id)).first()
    if not camera_query:
        raise PortError("","未找到此账号")
    channelNo = "1"
    if device_ids.find("-") != -1:
        channelNo = device_ids.split("-")[1]
        device_id = device_ids.split("-")[0]
    else:
        device_id = device_ids
    if camera_query.account_type == 1:
        appId = camera_query.app_key
        appSecret = camera_query.app_secret
        live_token = redis_pool.get(device_ids)
        if not live_token:
            live_data = getLive(appId, appSecret, device_id, channelNo)
            if live_data["result"]["code"] == "0":
                try:
                    live_token = live_data["result"]["data"]["streams"][0]["liveToken"]
                    redis_pool.set(device_ids,live_token,60*60*23)
                except:
                    raise PortError("","获取liveToken失败")
            else:
                raise PortError("",live_data)
    else:
        raise PortError("","其它设备没有此功能")
    if off_on == "on":
        if not start_time and not end_time:
            status = modify_live_time(appId,appSecret,live_token,start_time,end_time,"always")
        else:
            # 时间内开启直播
            status = modify_live_time(appId,appSecret,live_token,start_time,end_time)
    elif off_on == "off":
        status = off_on_live(appId,appSecret,live_token,off_on)
    else:
        raise PortError("","参数超出范围")
    if status["result"]["code"] == "0":
        device_query.device_name = device_name
        device_query.save()
        config = off_on + "-" + start_time + "-" + end_time
        redis_pool.set(device_ids+"config",config)
        return True
    else:
        return status
    

@kedong_deco(login_required=True)
def camera_takephoto(request):
    # 监控拍照接口
    device_id = request.POST.get("device_id")
    if not device_id:
        raise PortError("","设备号未传")
    try:
        d_id = MongoDevice.objects.get(device_id=device_id).id
    except:
        raise PortError("","未找出此设备")
    account_id = MongoCameraData.objects.filter(device_id=device_id).first()
    if not account_id:
        raise PortError("","未找到此设备")
    camera_query = MongoCameraAccount.objects.filter(id=int(account_id.account_id)).first()
    if not camera_query:
        raise PortError("","未找到此账号")
    channelNo = "0"
    if device_id.find("-") != -1:
        channelNo = device_id.split("-")[1]
        device_id = device_id.split("-")[0]
    else:
        device_id = device_id
    appId = camera_query.app_key
    appSecret = camera_query.app_secret
    snap_url = setDeviceSnap(appId, appSecret, device_id, channelNo)
    print(snap_url)
    # snap_url = "https://lechangecloud.oss-cn-hangzhou.aliyuncs.com/lechange/snappic/8H09715PHA657EC_img/Alarm/0/9d9913c5112c4587a391b6c454110c5a.jpg?Expires=1690882790&OSSAccessKeyId=LTAIP4igXeEjYBoG&Signature=RK97RSfcLjyA84Fv8aFOtA%2BvGao%3D"
    # snap_url = "https://lechangecloud.oss-cn-hangzhou.aliyuncs.com/lechange/snappic/8H09715PHA24E37_img/Alarm/0/ab85f10f33a34f068dc55a4a64e51584.jpg?Expires=1690883565&OSSAccessKeyId=LTAIP4igXeEjYBoG&Signature=%2FP1Bxksx0lagn24gkwaXWcS9Dno%3D"
    time.sleep(5)
    if snap_url:
        # 图片保存位置 
        config = settings.CONFIG
        save_path = "/jiankong/" + device_id + "/" + time.strftime("%Y%m%d", time.localtime())
        camera_path = config.get("media") + save_path
        os.makedirs(camera_path) if not os.path.exists(camera_path) else None
        stamp = int(time.time())
        unique_id = uuid.uuid4()
        combined_id = str(stamp) + "-" + str(unique_id)
        camera_photos = combined_id + ".jpg"
        img_path = os.path.join(camera_path, camera_photos)
        # 下载图片
        img_query = requests.get(snap_url, timeout=5)
        if img_query.status_code == 200:
            img = Image.open(BytesIO(img_query.content))
            img.save(img_path)
            camera_obj = MongoCameraPhoto.objects.create(
                device_id=d_id,
                addr=f"/media{save_path}/{camera_photos}",
                photo_status=1,
                addtime=int(time.time())
            )
            if camera_obj:
                return {"addr":camera_photos}
            else:
                raise PortError("","保存图片失败,稍后重试")
        else:
            raise PortError("","下载图片失败,稍后重试")
    else:
        raise PortError("","拍照失败,稍后重试")

    

@kedong_deco(login_required=True)
def camera_photo(request):
    # 拍照列表
    request_data = request.POST
    device_id = request_data.get("device_id")
    start_time = request_data.get("start_time","")
    end_time = request_data.get("end_time","")
    page_num = int(request_data.get("page")) if request_data.get("page") else 1
    page_size = int(request_data.get("pagesize")) if request_data.get("pagesize") else 10
    queryset = MongoCameraPhoto.objects.filter(device_id=device_id).order_by("-addtime")
    if start_time and end_time:
        queryset = queryset.filter(addtime__gte=start_time, addtime__lte=end_time)
    paginator = Paginator(queryset, page_size)
    page_obj = paginator.get_page(page_num)
    serializers = MongoCameraPhotoSerializers(page_obj, many=True)
    return {"data": serializers.data, "counts": queryset.count()}


@kedong_deco(login_required=True)
def get_camera_dh_tokens(request):
    """ 
    获取大华相机token 
    参数:
    device_id                必传(string)                   设备号
    """
    device_id = request.POST.get("device_id")
    if not device_id:
        raise PortError("","参数缺失")
    camera_query = MongoCameraData.objects.filter(device_id=device_id).first()
    if not camera_query:
        raise PortError("","未找到此设备")
    camera_account_query = MongoCameraAccount.objects.filter(id=camera_query.account_id).first()
    if not camera_account_query:
        raise PortError("","此设备未绑定监控账号")
    if camera_account_query.account_type == 0:
        raise PortError('', '海康监控暂不支持获取token')
    if device_id.find("-") != -1:
        channelNo = device_id.split("-")[1]
        device_id = device_id.split("-")[0]
    appId = camera_account_query.app_key
    appSecret = camera_account_query.app_secret
    like_token_data = getKitToken(appId, appSecret,device_id,channelNo)
    return like_token_data


@kedong_deco(login_required=True)
def get_camera_dh_token(request):
    """ 
    获取大华相机token 
    参数:
    device_id                必传(string)                   设备号
    """
    device_id = request.POST.get("device_id")
    if not device_id:
        raise PortError("","参数缺失")
    camera_query = MongoCameraData.objects.filter(device_id=device_id).first()
    if not camera_query:
        raise PortError("","未找到此设备")
    camera_account_query = MongoCameraAccount.objects.filter(id=camera_query.account_id).first()
    if not camera_account_query:
        raise PortError("","此设备未绑定监控账号")
    if camera_account_query.account_type == 0:
        raise PortError('', '海康监控暂不支持获取token')
    if device_id.find("-") != -1:
        channelNo = device_id.split("-")[1]
        device_id = device_id.split("-")[0]
    appId = camera_account_query.app_key
    appSecret = camera_account_query.app_secret
    like_token_data = getRecord(appId, appSecret,device_id,channelNo)
    return like_token_data