# -*- coding: utf-8 -*-
import json
import logging
import random

from sqlalchemy import text

from website import consts
from website import db_mysql, errors
from website.db.alg_model.alg_model import ModelRepositry as DB_AlgModel
from website.db.device_classification import device_classification as DB_DeviceClassification
from website.db.enterprise_busi_model import (
    enterprise_busi_model as DB_BusiModel,
)
from website.db.enterprise_busi_model import enterprise_busi_model_node_device as DB_BusiModelNodeDevice
from website.db.enterprise_device import enterprise_device as DB_Device
from website.db.enterprise_node import enterprise_node as DB_Node
from website.db.enterprise_node import enterprise_node_base_model_conf as DB_NodeBaseModelConf
from website.handler import APIHandler, authenticated, operation_log
from website.util import date_util
from website.util import shortuuid


class DeviceClassificationAddHandler(APIHandler):
    """
    - 描述:添加设备分类
    - 请求方式:post
    - 请求参数:
    >- entity_id, int, 企业id
    >- name, string, 设备分类名称
    - 返回值:
    ```
        {
            "id": 123
        }
    ```
    """

    @authenticated
    @operation_log("企业管理", "设备管理", consts.op_type_add_str, "添加设备分类")
    def post(self):
        name = self.get_escaped_argument("name", "")
        with self.app_mysql.connect() as conn:
            cur = conn.execute(
                text("SELECT id FROM device_classification WHERE name=:name and del=0"),
                {"name": name},
            )
            row = cur.fetchone()

            if row:
                raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备分类已存在")

            result = conn.execute(
                text(
                    "INSERT INTO device_classification (name, suid) VALUES (:name, :suid)"
                ),
                {"name": name, "suid": shortuuid.ShortUUID().random(10)},
            )
            conn.commit()
            last_id = result.lastrowid
        self.finish({"id": last_id})


class DeviceClassificationHandler(APIHandler):
    """
    - 描述:设备分类列表
    - 请求方式:post
    - 请求参数:
    >- 无
    - 返回值:
    ```
        {
            "data": [
                {
                    "id": 123,
                    "suid": "xxx",
                    "name": "xxx"
                },
                ...
            ]
        }
    ```
    """

    @authenticated
    @operation_log("企业管理", "设备管理", consts.op_type_list_str, "设备分类列表")
    def post(self):
        with self.app_mysql.connect() as conn:
            cur = conn.execute(
                text(
                    "SELECT id, suid, name FROM device_classification where del=0 ORDER BY id DESC"
                )
            )
            res = db_mysql.to_json_list(cur)
            res = res and res or []

        self.finish({"data": res})


class DeviceClassificationDeleteHandler(APIHandler):
    """
    - 描述:删除设备分类
    - 请求方式:post
    - 请求参数:
    >- id
    - 返回值:无
    """

    @authenticated
    @operation_log("企业管理", "设备管理", consts.op_type_delete_str, "删除设备分类")
    def post(self):
        did = self.get_int_argument("id")

        with self.app_mysql.connect() as conn:
            cur = conn.execute(
                text(
                    """
                select d.id from enterprise_device d, device_classification c 
                where d.classification=c.suid and c.id=:id and c.del=0
                """
                ),
                {"id": did},
            )
            rows = cur.fetchall()
            if rows:
                raise errors.HTTPAPIError(
                    errors.ERROR_BAD_REQUEST, "该分类使用中,无法删除"
                )

            conn.execute(
                text("update device_classification set del=1 WHERE id=:id"), {"id": did}
            )
            conn.commit()
        self.finish()


class DeviceClassificationEditHandler(APIHandler):
    """
    - 描述:编辑设备分类
    - 请求方式:post
    - 请求参数:
    >- id
    >- name
    - 返回值:无
    """

    @authenticated
    @operation_log("企业管理", "设备管理", consts.op_type_edit_str, "编辑设备分类")
    def post(self):
        did = self.get_int_argument("id")
        name = self.get_escaped_argument("name", "")

        with self.app_mysql.connect() as conn:
            conn.execute(
                text("update device_classification set name=:name WHERE id=:id"),
                {"id": did, "name": name},
            )
            conn.commit()
        self.finish()

class DeviceAddHandler(APIHandler):
    """
    - 描述:企业节点,添加设备
    - 请求方式:post
    - 请求参数:
    >- entity_id, int, 企业id
    >- node_id, int
    >- name, string, 设备名称
    >- addr, string, 设备位置
    >- classification, string, 设备分类
    >- device_model, string, 设备型号
    >- param, string, 设备参数
    >- comment, string, 备注
    - 返回值:
    ```
        无
    ```
    """

    @authenticated
    @operation_log("企业管理", "设备管理", consts.op_type_add_str, "企业节点,添加设备")
    def post(self):
        entity_id = self.get_int_argument("entity_id")
        node_id = self.get_int_argument("node_id")
        name = self.get_escaped_argument("name", "")
        addr = self.get_escaped_argument("addr", "")
        classification = self.get_escaped_argument("classification", "")
        device_model = self.get_escaped_argument("device_model", "")
        param = self.get_escaped_argument("param", "")
        comment = self.get_escaped_argument("comment", "")

        if not name:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备名称不能为空")
        if not entity_id or not node_id:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空")

        # 判断设备分类是否存在
        db_classification = DB_DeviceClassification.DeviceClassificationReporitory()
        row = db_classification.get_row_by_suid(classification)
        if not row:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备分类不存在")

        device_data = {
            "entity_id": entity_id,
            "node_id": node_id,
            "name": name,
            "addr": addr,
            "classification": classification,
            "device_model": device_model,
            "param": param,
            "comment": comment,
        }
        db_device = DB_Device.EnterpriseDeviceRepository()
        db_device.add_device(device_data)

        self.finish()


class DeviceEditHandler(APIHandler):
    """
    - 描述:企业节点,编辑设备
    - 请求方式:post
    - 请求参数:
    >- device_id, int, 设备id
    >- name, string, 设备名称
    >- addr, string, 设备位置
    >- classification, string, 设备分类的short uuid
    >- device_model, string, 设备型号
    >- param, string, 设备参数
    >- comment, string, 备注
    - 返回值:无
    """

    @authenticated
    @operation_log("企业管理", "设备管理", consts.op_type_edit_str, "企业节点,编辑设备")
    def post(self):
        device_id = self.get_int_argument("device_id")
        name = self.get_escaped_argument("name", "")
        addr = self.get_escaped_argument("addr", "")
        classification = self.get_escaped_argument("classification", "")
        device_model = self.get_escaped_argument("device_model", "")
        param = self.get_escaped_argument("param", "")
        comment = self.get_escaped_argument("comment", "")
        if not device_id:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备id不能为空")
        if not name:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备名称不能为空")

        device_data = {
            "id": device_id,
            "name": name,
            "addr": addr,
            "classification": classification,
            "device_model": device_model,
            "param": param,
            "comment": comment,
        }

        db_device = DB_Device.EnterpriseDeviceRepository()
        db_device.edit_device(device_data)
        self.finish()


class DeviceDeleteHandler(APIHandler):
    """
    - 描述:企业节点,删除设备
    - 请求方式:post
    - 请求参数:
    >- node_id, int
    >- device_id, int, 设备id
    - 返回值:无
    """

    @authenticated
    @operation_log("企业管理", "设备管理", consts.op_type_delete_str, "企业节点,删除设备")
    def post(self):
        # node_id = self.get_int_argument('node_id')
        device_id = self.get_int_argument("device_id")
        if not device_id:
            raise errors.HTTPAPIError(
                errors.ERROR_BAD_REQUEST, "企业节点或设备不能为空"
            )
        db_device = DB_Device.EnterpriseDeviceRepository()
        db_device.delete_device(device_id)
        self.finish()


class DeviceListHandler(APIHandler):
    """
    - 描述:企业节点,设备列表
    - 请求方式:post
    - 请求参数:
    > - pageNo
    > - pageSize
    > - node_id, int, 节点id
    - 返回值:
    ```
        {
            "count": 123,
            "data": [
                {
                    "device_id":  123,
                    "device_name": "xxx",
                    "device_class": "xxx", 设备类型
                    "deployed": 0, # 是否部署, 0/未部署, 1/已部署
                },
                ...
            ]
        }
    ```
    """

    @authenticated
    @operation_log("企业管理", "设备管理", consts.op_type_list_str, "企业节点,设备列表")
    def post(self):
        node_id = self.get_int_argument("node_id")
        pageNo = self.get_int_argument("pageNo", 1)
        pageSize = self.get_int_argument("pageSize", 20)
        if not node_id:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空")
        db_device = DB_Device.EnterpriseDeviceRepository()
        devices = db_device.list_devices(
            node_id=node_id, pageNo=pageNo, pageSize=pageSize
        )

        logging.info(devices)

        self.finish({"count": devices["total_count"], "data": devices["devices"]})


class DeviceListSimpleHandler(APIHandler):
    """获取节点下所有设备的简单信息"""

    @authenticated
    def post(self):
        node_id = self.get_int_argument("node_id")
        if not node_id:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空")
        db_device = DB_Device.EnterpriseDeviceRepository()
        devices = db_device.list_simple_devices(node_id=node_id)
        logging.info(devices)
        self.finish({"data": devices})


class DeviceInfoHandler(APIHandler):
    """
    - 描述:企业节点,设备信息
    - 请求方式:post
    - 请求参数:
    > - device_id, int, 设备id
    - 返回值:
    ```
        {
            "name": "xxx",
            "addr": "xxx",
            "classification": "xxx",
            "device_model": "xxx",
            "param": "xxx",
            "comment": "xxx",
        }
    ```
    """
    @authenticated
    @operation_log("企业管理", "设备管理", consts.op_type_list_str, "企业节点,设备信息")
    def post(self):
        device_id = self.get_int_argument("device_id")
        if not device_id:
            raise errors.HTTPAPIError(
                errors.ERROR_BAD_REQUEST, "企业节点或设备不能为空"
            )
        db_device = DB_Device.EnterpriseDeviceRepository()
        device = db_device.get_device(device_id=device_id)
        logging.info(device)
        self.finish(device)


class DeviceBasemodelListHandler(APIHandler):
    """
    - 描述:企业节点,节点信息 -> 设备列表 -> 基础模型配置 -> 模型列表
    - 请求方式:post
    - 请求参数:
    > - pageNo
    > - pageSize
    > - device_id, int, 设备id
    - 返回值:
    ```
        {
            "count": 123,
            "data": [
                {
                    "busi_model": "xxx",      # 业务模型的名称
                    "base_model": [
                        {
                            "model_id": 123,           # 基础模型id
                            "model_name": "xxx"        # 基础模型name
                            "model_version": "xxx",    # 基础模型的版本
                            "model_hub_image": "xxx",      # 运行库镜像
                        },
                        ...
                    ]
                },
                ...
            ]
        }
    ```
    """

    @authenticated
    @operation_log("企业管理", "节点设置", consts.op_type_list_str, "企业节点,节点信息 -> 设备列表 -> 基础模型配置 -> 模型列表")
    def post(self):
        device_id = self.get_int_argument("device_id")
        if not device_id:
            raise errors.HTTPAPIError(
                errors.ERROR_BAD_REQUEST, "企业节点或设备不能为空"
            )
        pageNo = self.get_int_argument("pageNo", 1)
        pageSize = self.get_int_argument("pageSize", 10)
        db_busi_model = DB_BusiModelNodeDevice.EnterpriseBusiModelNodeDeviceRepository()
        busi_models, count = db_busi_model.get_busi_model_by_device(device_id=device_id, pagination=True,
                                                                    page_no=pageNo,
                                                                    page_size=pageSize)
        if count == 0:
            self.finish({"count": count, "data": []})
            return

        res = []
        for item in busi_models:
            busi_model_id = item["busi_model_id"]
            busi_model_name = item["name"]
            base_model_list = json.loads(item["base_models"])
            base_models = []
            for base_model in base_model_list:
                base_model_id = base_model["id"]
                base_model_suid = base_model["suid"]
                base_model_name = base_model["name"]
                db_alg_model = DB_AlgModel()
                base_model = db_alg_model.get_model_dict_by_id(base_model_id)
                base_model_version = base_model["default_version"]
                db_conf = DB_NodeBaseModelConf.EnterpriseNodeDeviceBMCusConfRepository()
                conf = db_conf.get_busi_model_custom_config(
                    node_id=item["node_id"],
                    device_id=device_id,
                    busi_model_id=busi_model_id,
                    base_model_id=base_model_id
                    )
                base_model_hub_image = ""
                if conf:
                    base_model_hub_image = conf.model_hub_image
                base_models.append({
                    "model_id": base_model_id,
                    "model_name": base_model_name,
                    "model_version": base_model_version,
                    "model_hub_image": base_model_hub_image,
                })
            res.append({
                "busi_model_id": busi_model_id,
                "busi_model_name": busi_model_name,
                "base_models": base_models
            })

        self.finish({"count": count, "data": res})


class DeviceBaseModelCustomConfigHandler(APIHandler):
    """
    - 描述:企业节点,节点信息 -> 设备列表 -> 基础模型配置 -> 基础模型参数配置
    - 请求方式:post
    - 请求参数:
    > - device_id, int, 设备id
    > - node_id, int, 节点id
    > - base_model_id, int, 基础模型id
    > - busi_conf_file, string, 业务参数配置文件md5
    > - busi_conf_str, string, 业务参数配置,json字符串,eg:'{"key1": "value1", "key2": "value2"}'
    > - model_hub_image, string, 运行库地址
    > - model_conf_file, string, 模型参数配置文件md5
    > - model_conf_str, string, 模型参数配置,json字符串,eg:'{"key1": "value1", "key2": "value2"}'
    - 返回值:无
    """

    @authenticated
    @operation_log("企业管理", "节点设置", consts.op_type_add_str,
                   "企业节点,节点信息 -> 设备列表 -> 基础模型配置 -> 基础模型参数配置")
    def post(self):
        device_id = self.get_int_argument("device_id")
        node_id = self.get_int_argument("node_id")
        busi_model_id = self.get_int_argument("busi_model_id")
        base_model_id = self.get_int_argument("base_model_id")
        busi_conf_file = self.get_escaped_argument("busi_conf_file")
        busi_conf_str = self.get_escaped_argument("busi_conf_str")
        model_hub_image = self.get_escaped_argument("model_hub_image")
        model_conf_file = self.get_escaped_argument("model_conf_file")
        model_conf_str = self.get_escaped_argument("model_conf_str")

        db_device = DB_Device.EnterpriseDeviceRepository()
        device = db_device.get_device(device_id=device_id)
        if not device:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "device not exist")
        if device["node_id"] != node_id:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "device not belong to this node")
        db_alg_model = DB_AlgModel()
        base_model = db_alg_model.get_model_dict_by_id(base_model_id)
        if not base_model:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "base model not exist")

        device_suid = device["suid"]
        node_suid = device["node_suid"]
        entity_id = device["entity_id"]
        entity_suid = device["entity_suid"]

        db_busi_model = DB_BusiModel.EnterpriseBusiModelRepository()
        busi_model = db_busi_model.get_busi_model_by_id(busi_model_id)
        busi_model_suid = busi_model.suid
        data = dict()
        data.update(
            entity_id=entity_id,
            entity_suid=entity_suid,
            node_id=node_id,
            node_suid=node_suid,
            device_id=device_id,
            device_suid=device_suid,
            busi_model_id=busi_model_id,
            busi_model_suid=busi_model_suid,
            base_model_id=base_model_id,
            base_model_suid=base_model["suid"],
            busi_conf_file=busi_conf_file,
            busi_conf_str=busi_conf_str,
            model_hub_image=model_hub_image,
            model_conf_file=model_conf_file,
            model_conf_str=model_conf_str
        )
        db_conf = DB_NodeBaseModelConf.EnterpriseNodeDeviceBMCusConfRepository()
        db_conf.create_busi_model_custom_config(data=data)

        self.finish()


class StatusListHandler(APIHandler):
    """
    - 描述:设备状态列表
    - 请求方式:post
    - 请求参数:
    > - entity_id, int, 企业id
    > - classification_suid, string, 分类id
    > - status, int, 状态,1000/all/默认, 1001/在线,1002/离线,1003/运行中,1004/故障
    > - pageNo
    > - pageSize
    - 返回值:无
    ```
    {
        "count": 123,
        "data": [
            {
                "id": 123,
                "name": "xxx",
                "status": 1001,
                "cpu": 123,
                "mem": 123,
                "storage": 123,
                "gpu": 123,
            },
            ...
        ]
    }
    ```
    """

    @authenticated
    @operation_log("企业管理", "设备状态", consts.op_type_list_str,"设备状态列表")
    def post(self):
        entity_id = self.get_int_argument("entity_id")
        group = self.get_int_argument("group")
        classification = self.get_escaped_argument("classification", "")
        status = self.get_int_argument("status", 1000)
        pageNo = self.get_int_argument("pageNo", 1)
        pageSize = self.get_int_argument("pageSize", 20)

        if not entity_id:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空")
        if status not in consts.device_status_map:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "状态参数错误")

        db_device = DB_Device.EnterpriseDeviceRepository()
        res = db_device.list_entity_devices(
            entity_id=entity_id,
            pageno=pageNo,
            pagesize=pageSize,
            classification=classification,
            status=status,
            group=group
        )

        count = res["count"]
        devices = res["devices"]
        data = []
        for item, _ in devices:
            logging.info(item)
            data.append(
                {
                    "id": item.id,
                    "name": item.name,
                    "status": item.status,
                    "cpu": random.randint(20, 30),
                    "mem": random.randint(20, 30),
                    "storage": random.randint(20, 30),
                    "gpu": random.randint(20, 30),
                }
            )

        status_dic = {
            consts.device_status_online: 0,
            consts.device_status_offline: 0,
            consts.device_status_ongoing: 0,
            consts.device_status_error: 0,
        }
        status_count = db_device.status_count(entity_id=entity_id, classification=classification)
        for key in status_count:
            status_dic.update({key: status_count[key]})

        self.finish({"count": count, "data": data, "status_count": status_dic})


class StatusGroupHandler(APIHandler):
    @authenticated
    def post(self):
        entity_id = self.get_int_argument("entity_id")
        db_node = DB_Node.EnterpriseNodeRepository()
        res = db_node.simple_list(entity_id)
        self.finish({"result": res})


class StatusInfoHandler(APIHandler):
    """ """

    @authenticated
    @operation_log("企业管理", "设备状态", consts.op_type_list_str, "设备信息")
    def post(self):
        device_id = self.get_int_argument("id")
        if not device_id:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备id不能为空")

        # 查询设备信息
        db_device = DB_Device.EnterpriseDeviceRepository()
        res = db_device.get_devices(device_ids=[device_id])
        if not res:
            raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备不存在")
        res = res[0]
        node_id = res["node_id"]
        device_suid = res["suid"]
        device_name = res["name"]
        device_comment = res["comment"]
        classification = res["classification"]
        db_cls = DB_DeviceClassification.DeviceClassificationReporitory()
        row_cls = db_cls.get_row_by_suid(suid=classification)
        device_classification_name = row_cls.name

        db_node = DB_Node.EnterpriseNodeRepository()
        node_info = db_node.get_node_by_id(node_id)
        node_name = node_info["name"]

        db_busi_model = DB_BusiModelNodeDevice.EnterpriseBusiModelNodeDeviceRepository()
        busi_models, _ = db_busi_model.get_busi_model_by_device(device_id=device_id)

        models = []
        for item in busi_models:
            busi_model_id = item["busi_model_id"]
            busi_model_name = item["name"]
            base_model_list = json.loads(item["base_models"])
            base_models = []
            for base_model in base_model_list:
                base_model_id = base_model["id"]
                base_model_name = base_model["name"]
                db_alg_model = DB_AlgModel()
                base_model = db_alg_model.get_model_dict_by_id(base_model_id)
                base_model_version = base_model["default_version"]
                db_conf = DB_NodeBaseModelConf.EnterpriseNodeDeviceBMCusConfRepository()
                conf = db_conf.get_busi_model_custom_config(busi_model_id=busi_model_id, device_id=device_id,
                                                            node_id=item["node_id"])
                base_model_hub_image = ""
                if conf:
                    base_model_hub_image = conf.model_hub_image
                base_models.append({
                    "model_id": base_model_id,
                    "model_name": base_model_name,
                    "model_version": base_model_version,
                    "model_hub_image": base_model_hub_image,
                })
            models.append({
                "busi_model": busi_model_name,
                "base_models": base_models
            })

        self.finish({
            "classification": device_classification_name,
            "name": device_name,
            "comment": device_comment,
            "ID": device_suid,
            "cpu": random.randint(20, 30),
            "mem": random.randint(20, 30),
            "storage": random.randint(20, 30),
            "gpu": random.randint(20, 30),
            "models": models,
            "group": node_name
        })


class StatusLogHandler(APIHandler):
    """
    - 描述:设备状态日志
    - 请求方式:post
    - 请求参数:
    > - entity_id, int, 企业id
    > - pageNo
    > - pageSize
    - 返回值:
    ```
        {
            "count": 123,
            "data": [
                {
                    "ID": "xxx",
                    "name": "xxx",
                    "classification": "xxx",
                    "IP": "xxx",
                    "duration": "xxx",
                }
            ]
        }
```
"""

    @authenticated
    def post(self):
        entity_id = self.get_int_argument("entity_id")
        pageNo = self.get_int_argument("pageNo", 1)
        pageSize = self.get_int_argument("pageSize", 10)

        db_device = DB_Device.EnterpriseDeviceRepository()
        res = db_device.list_entity_devices(entity_id=entity_id, pageno=pageNo, pagesize=pageSize)
        count = res["count"]
        devices = res["devices"]

        result = []
        for device, classification_name in devices:
            device_name = device.name
            device_suid = device.suid
            device_ip = ""
            duration = date_util.time_diff(str(device.create_time))
            result.append({
                "ID": device_suid,
                "name": device_name,
                "classification": classification_name,
                "IP": device_ip,
                "duration": duration,
            })

        self.finish({"count": count, "data": result})