import datetime

import requests
from addict import Dict
from uuid import uuid4, uuid1
from hashlib import md5
import time
import json

from common.third_party_api.surveillance.base import (ApiAuthenticationError,
                                                      BaseConfiguration,
                                                      checkAuthentication,
                                                      session, Surveillance)
from common.utils.exceptions import ApiException


class Configuration(BaseConfiguration):
    def __init__(self, instance):
        """
        :param instance: django orm class
        """
        self.default_json_headers = {
            "User-Agent":
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) "
                "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0."
                "4147.135 Safari/537.36",
            "Content-Type":
                "application/json;charset=UTF-8",
            "Connection":
                "close"
        }
        self.session = session
        self.instance = instance
        self.app_key = instance.app_key
        self.app_secret = instance.app_secret
        self.access_token = instance.access_token
        self.home_url = instance.home_url

    def generate_nonce(self):
        return uuid1().hex

    def generate_sign(self, time, nonce):
        dict_sort = "time:" + str(
            time) + ",nonce:" + nonce + ",appSecret:" + self.app_secret
        return md5(dict_sort.encode()).hexdigest()

    @checkAuthentication
    def request(self, action, re_params=None, method="POST", *args, **kwargs):
        default_data = self.req_data(re_params)
        params = kwargs.get("params", {})
        data = kwargs.get("data", {})
        headers = kwargs.get("headers", {})
        data.update(default_data)
        headers.update(self.default_json_headers)
        url = "/".join([self.instance.home_url.rstrip("/"), "openapi", action])
        try:
            rst = self.session.request(method=method,
                                       url=url,
                                       data=json.dumps(data),
                                       params=params,
                                       *args,
                                       **kwargs)
            if rst.status_code != 200:
                raise ApiException(msg_show="调用乐橙云api失败, 请检查配置")
        except Exception as e:
            raise ApiException(msg=e)
        rst = rst.json()
        code = rst["result"].get("code")
        if code != '0':
            if code == "TK1002" or code == "OP1007":
                raise ApiAuthenticationError
            raise ApiException(msg_show="访问乐橙云参数错误，请检查配置")
        return rst["result"]["data"]

    def req_system(self):
        nonce = self.generate_nonce()
        tm = str(int(time.time()))
        sign = self.generate_sign(tm, nonce)
        return {
            "ver": "1.0",
            "nonce": nonce,
            "time": tm,
            "sign": sign,
            "appId": self.app_key,
        }

    def req_data(self, params=None):
        dt = {
            "id": str(uuid4()),
            "system": self.req_system(),
            "params": {
                "token": self.access_token
            },
        }
        if params:
            params.pop("token", None)
            dt["params"].update(params)
        return dt

    def set_access_token(self):
        data = {
            "id": str(uuid4()),
            "system": self.req_system(),
            "params": None
        }
        try:
            rst = self.session.post(url="/".join(
                [self.instance.home_url.rstrip("/"), "openapi/accessToken"]),
                headers=self.default_json_headers,
                data=json.dumps(data))
            if rst.status_code != 200:
                raise ApiException(msg_show="调用乐橙云api失败, 请检查配置")
        except Exception as e:
            raise ApiException(msg=e)
        rst = rst.json()
        code = rst["result"].get("code")
        if int(code) != 0:
            raise ApiException(msg_show="访问乐橙云参数错误，请检查配置")
        self.access_token = rst["result"]["data"]["accessToken"]
        self.instance.access_token = self.access_token
        self.instance.save()


class APIClient(Surveillance, Configuration):
    def list_account(self):
        return self.request(action="listSubAccount")

    def list_device_list(self):
        return self.request(action="deviceBaseList", re_params={
            "bindId": -1,
            "limit": 10,
            "type": "bindAndShare",
            "needApInfo": "false",
        })

    def list_cameras(self):
        raise

    def camera_info(self):
        raise

    def camera_status(self):
        raise

    def live_stream(self):
        raise

    def playback_stream(self):
        raise

    def create_device(self, **kwargs):
        """
        添加设备
        :param kwargs:
        :return:
        """
        param = {
            "data": {
                "token": self.access_token,
                "deviceId": kwargs.get("deviceSerial"),
                "code": kwargs.get("validateCode")
            }
        }
        res = self.request(action="bindDevice", **param)
        return res

    def get_video_url(self, **kwargs):
        """
        获取设备视频地址
        :param kwargs:
        :return:
        """
        url_list = []
        if kwargs.get("type") == "1":
            res = self.getLiveStreamInfo(
                **{
                    "deviceSerial": kwargs.get("deviceSerial"),
                    "channelNo": kwargs.get("channelNo", "0")
                })
            for item in res.get("streams", []):
                url_list.append({
                    "token": item.get("liveToken"),
                    "url": item.get("hls")
                })
        elif kwargs.get("type") == "2":
            res = self.queryLocalRecords(
                **{
                    "deviceId": kwargs.get("deviceSerial"),
                    "channelId": kwargs.get("channelNo", "0"),
                    "beginTime": kwargs.get("startTime"),
                    "endTime": kwargs.get("stopTime"),
                })
            for item in res.get("records", []):
                url_list.append(
                    {
                        "token": None,
                        "url": item.get("recordId")
                    }
                )

        else:
            token_list = self.get_video_token(
                **{
                    "beginTime": kwargs.get("startTime"),
                    "endTime": kwargs.get("stopTime"),
                    "deviceId": kwargs.get("deviceSerial"),
                    "channelId": kwargs.get("channelNo", "0"),
                }
            )
            token = None
            for item in token_list:
                if item.get("deviceId") == kwargs.get("channelId"):
                    token = item.get("token")
                    break

            url = self.get_video_path_by_token(**{
                "deviceId": kwargs.get("deviceId"),
                "token": token
            })
            url_list.append({
                "token": None,
                "url": url
            })

        return url_list

    def bindDeviceLive(self, **kwargs):
        """
        创建设备源直播地址
        :param kwargs:
        :return:
        """
        param = {
            "data": {
                "token": self.access_token,
                "deviceId": kwargs.get("deviceSerial"),
                "channelId": kwargs.get("channelNo", "0"),
                "streamId": kwargs.get("streamId", 1),
            }
        }
        res = self.request(action="bindDeviceLive", **param)
        return res

    def getLiveStreamInfo(self, **kwargs):
        """
        根据设备序列号获取直播地址
        :param kwargs:
        :return:
        """
        param = {
            "data": {
                "token": self.access_token,
                "deviceId": kwargs.get("deviceSerial"),
                "channelId": kwargs.get("channelNo", "0")
            }
        }
        res = self.request(action="getLiveStreamInfo", re_params=param)
        return res

    def queryLocalRecords(self, **kwargs):
        """
        获取设备本地录像视频
        :param kwargs:
        :return:
        """
        param = {
            "data": {
                "token": self.access_token,
                "deviceId": kwargs.get("deviceSerial"),
                "channelId": kwargs.get("channelNo", "0"),
                "beginTime": kwargs.get("startTime"),
                "endTime": kwargs.get("stopTime"),
                "type": "All",
                "queryRange": "1-30"
            }
        }
        res = self.request(action="queryLocalRecords", re_params=param)
        return res

    def queryCloudRecords(self, **kwargs):
        param = {
            "data": {
                "token": self.access_token,
                "deviceId": kwargs.get("deviceSerial"),
                "channelId": kwargs.get("channelNo", "0"),
                "beginTime": kwargs.get("startTime"),
                "endTime": kwargs.get("stopTime"),
                "type": "All",
                "queryRange": "1-30"
            }
        }
        res = self.request(action="queryCloudRecords", re_params=param)
        return res

    def get_device_list(self, **kwargs):
        param = {
            "token": self.access_token,
            "bindId": "-1",
            "limit": 128,
            "type": "share",
            "needApInfo": "false"

        }
        res = self.request(action="deviceBaseList", re_params=param)
        return res

    def get_video_token(self, **kwargs):
        data = []
        params = {
            "count": "10",
            "beginTime": kwargs.get("startTime"),
            "endTime": kwargs.get("stopTime"),
            "deviceId": kwargs.get("deviceSerial"),
            "channelId": kwargs.get("channelNo", "0"),
            "nextAlarmId": "-1",
            "token": self.access_token
        }
        res = self.request(action="getAlarmMessage", re_params=params)
        for item in res.get("alarms", []):
            data.append({
                "deviceId": item.get("deviceId"),
                "token": item.get("token")
            })
        return data

    def get_video_path_by_token(self, **kwargs):
        params = {
            "token": self.access_token,
            "deviceId": kwargs.get("deviceId"),
            "cloudToken": kwargs.get("token")
        }
        res = self.request(action="queryCloudRecordByToken", re_params=params)
        url = res.get("recordPath")
        return url

    def bindDeviceLive(self, **kwargs):
        param = {
            "streamId": 1,
            "deviceId": kwargs.get("deviceId"),
            "channelId": kwargs.get("channelId", "0"),
            "token": self.access_token
        }
        res = self.request(action="bindDeviceLive", re_params=param)
        return res


if __name__ == "__main__":
    instance = Dict({
        "app_key": "lcebe4125360774db0",
        "app_secret": "63f6abbaedea447b99cdf49475e225",
        "home_url": "https://openapi.lechange.cn",
        "access_token": "At_0000c72e67f5d0e641989c4ac7e84241",
    })
    a = APIClient(instance)
    print(a.list_account())
