# -*- coding:utf-8 -*-
"""
@File    :   meian_db
@Author  :   xuxingchen
@Version :   1.0
@Contact :   xuxingchen@sinochem.com
@Desc    :   meian database crud
"""
import csv
import json
import os.path
import sqlite3
import time
import traceback

from logger import Logger, new_dc
from datetime import datetime
from devices.meian_model import HeartBeat, Register, PushRtAccessRecord
from devices.common_model import UserInfo
from utils import datetime_to_timestamp
from config import ENV_TYPE, DEBUG


class SQLiteDatabaseEngine:
    def __init__(self, db_path: str = "demo.db") -> None:
        self.sqlite3 = sqlite3
        self.db_path = db_path
        self.connection = None
        self.cursor = None
        self.connect()

    def __del__(self):
        # self.disconnect()
        pass

    def connect(self):
        """连接SQLite 数据库（如果数据库不存在则会自动创建）"""
        self.connection = self.sqlite3.connect(self.db_path)
        self.cursor = self.connection.cursor()
        Logger.init(new_dc(f"🔗 SQLite - {self.db_path} has connect successfully! 🔗", "[1;32m"))

    def disconnect(self):
        try:
            self.cursor.close()
            self.connection.close()
        except Exception as e:
            if type(e).__name__ != "ProgrammingError":
                Logger.error(f"{type(e).__name__}, {e}")
        Logger.info(new_dc(f"🔌 Disconnect from SQLite - {self.db_path}! 🔌", "[1m"))

    def exist(self, table_name):
        self.cursor.execute(
            f"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
            (table_name,),
        )
        result = self.cursor.fetchone()
        if result:
            return True
        else:
            return False


class BaseTable:
    def __init__(self, connection=None, cursor=None):
        self.connection = connection
        self.cursor = cursor

    def set(self, connection, cursor):
        self.connection = connection
        self.cursor = cursor

    def execute(self, sql: str, params: tuple = ()):
        self.cursor.execute(sql, params)
        self.connection.commit()

    def executemany(self, sql: str, params: list[tuple]):
        self.cursor.executemany(sql, params)
        self.connection.commit()

    def query(self, sql: str, params: tuple = ()):
        self.cursor.execute(sql, params)


class DeviceTable(BaseTable):
    @staticmethod
    def check(table_handler: BaseTable):
        """检测是否存在当前表，并根据devices.csv开始数据初始化"""
        table_handler.query("SELECT name FROM sqlite_master WHERE type='table' AND name='device'")
        if table_handler.cursor.fetchone() is None:
            table_handler.execute(
                f"""
                CREATE TABLE device (
                    device_id TEXT,
                    project_code TEXT,
                    subscribe_topic TEXT,
                    publish_topic TEXT,
                    aiot_id TEXT NULL,
                    register_type INTEGER default 0,
                    PRIMARY KEY (device_id)
                )
                """
            )
        init_config_path = os.path.join(os.path.dirname((os.path.abspath("__file__"))), "data",
                                        "_devices.csv" if ENV_TYPE != 2 else "devices.csv")
        if os.path.exists(init_config_path):
            with open(init_config_path, newline='', encoding="utf8") as csvfile:
                csvreader = csv.reader(csvfile)
                head = next(csvreader)
                data = []
                if len(head) == 5:
                    for row in csvreader:
                        register_type = 0 if row[4] == '' else 1
                        data.append(tuple([i.strip() for i in row] + [register_type]))

                    table_handler.executemany(
                        f"""
                        INSERT INTO device 
                        (project_code, device_id, subscribe_topic, publish_topic, aiot_id, register_type) 
                        VALUES (?, ?, ?, ?, ?, ?) 
                        ON CONFLICT (device_id) DO NOTHING
                        """,
                        data
                    )

    @staticmethod
    def get_topics(table_handler):
        table_handler.query(
            """
            SELECT (
                SELECT json_group_array(subscribe_topic)
                FROM (
                    SELECT DISTINCT subscribe_topic
                    FROM device
                )
            ) AS subscribe_topics,
            (
                SELECT json_group_array(publish_topic)
                FROM (
                    SELECT DISTINCT publish_topic
                    FROM device
                )
            ) AS publish_topics;
            """
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res[0]
        else:
            return None

    @staticmethod
    def check_device_id(table_handler, topic, device_id):
        """device in `publish_topic` and `device_id` column"""
        table_handler.query(
            f"""
            SELECT DISTINCT device_id from device where publish_topic = '{topic}' and device_id = '{device_id}'
            """
        )
        if len(table_handler.cursor.fetchall()) > 0:
            return True
        else:
            return False

    @staticmethod
    def get_device_topic(table_handler, device_id):
        """获取对应设备的订阅主题"""
        table_handler.query(
            f"""
            SELECT subscribe_topic, publish_topic from device where device_id = '{device_id}'
            """
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res[0]
        else:
            return None

    @staticmethod
    def get_device_register_type(table_handler, device_id):
        table_handler.query(
            f"""
            SELECT register_type from device where device_id = '{device_id}'
            """
        )
        res = table_handler.cursor.fetchall()
        if res and res[0][0] == 1:
            return True
        else:
            return False

    @staticmethod
    def get_aiot_id(table_handler, device_id):
        table_handler.query(
            f"""
            SELECT aiot_id from device where device_id = '{device_id}' and register_type = 1
            """
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res[0][0]
        else:
            return None

    @staticmethod
    def get_device_id(table_handler, aiot_id):
        table_handler.query(
            f"""
            SELECT device_id from device where aiot_id = '{aiot_id}' and register_type = 1
            """
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res[0][0]
        else:
            return None

    @staticmethod
    def get_device_ids(table_handler):
        """获取所有设备的ID"""
        table_handler.query(
            f"""
            SELECT device_id from device
            """
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res
        else:
            return None

    @staticmethod
    def get_project_code(table_handler, device_id):
        table_handler.query(
            f"""
            SELECT project_code from device where device_id = '{device_id}'
            """
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res[0][0]
        else:
            return None

    @staticmethod
    def update_aiot_id(table_handler: BaseTable, device_id, aiot_id):
        table_handler.execute(
            f"""
            UPDATE device SET aiot_id = '{aiot_id}', register_type = 1 WHERE device_id = '{device_id}'
            """
        )


class GatewayTable(BaseTable):
    @staticmethod
    def check(table_handler: BaseTable):
        """检测是否存在当前表，并根据gateway.json开始数据初始化"""
        table_handler.query("SELECT name FROM sqlite_master WHERE type='table' AND name='gateway'")
        if table_handler.cursor.fetchone() is None:
            table_handler.execute(
                f"""
                CREATE TABLE gateway (
                    gateway_id TEXT NULL,
                    project_code TEXT,
                    gateway_sct TEXT NULL,
                    register_type INTEGER default 0,
                    PRIMARY KEY (gateway_id)
                )
                """
            )
        init_config_path = os.path.join(os.path.dirname(os.path.abspath("__file__")), "data",
                                        "_gateways.json" if ENV_TYPE != 2 else "gateways.json")
        if os.path.exists(init_config_path):
            with open(init_config_path, "r", encoding="utf8") as f:
                try:
                    gateway_config = json.load(f)
                    data = []
                    for project_code, gateway_info in gateway_config.items():
                        data.append((project_code, gateway_info[0], gateway_info[1], 1))
                    table_handler.executemany(
                        f"""
                        INSERT INTO gateway (project_code, gateway_id, gateway_sct, register_type) VALUES (?, ?, ?, ?) 
                        ON CONFLICT (gateway_id) DO NOTHING
                        """,
                        data
                    )
                except Exception as e:
                    Logger.error(f"{type(e).__name__}, {e}")
                    if DEBUG:
                        traceback.print_exc()

    @staticmethod
    def get_gateway(table_handler, project_code):
        table_handler.query(
            f"""
            SELECT gateway_id, gateway_sct from gateway where project_code = '{project_code}'
            """
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res[0]
        else:
            return None, None

    @staticmethod
    def get_registered_gateway(table_handler):
        table_handler.query(
            f"""
            SELECT gateway_id, gateway_sct from gateway where register_type = 1
            """
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res
        else:
            return None

    @staticmethod
    def get_project_code_list(table_handler):
        table_handler.query(
            f"""
            SELECT DISTINCT project_code from gateway
            """
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res
        else:
            return []

    @staticmethod
    def get_registered_sub_aiot_id(table_handler, gateway_id):
        """查询已注册网关下的所有子设备在aiot平台的设备id"""
        table_handler.query(
            f"""
            SELECT json_group_array(aiot_id) FROM device WHERE project_code IN 
            (SELECT DISTINCT project_code FROM gateway 
            WHERE register_type = 1 and gateway_id = '{gateway_id}') 
            AND register_type = 1
            """
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res[0]
        else:
            return None


class HeartBeatTable(BaseTable):
    @staticmethod
    def check(table_handler: BaseTable):
        table_handler.execute(
            """
            CREATE TABLE IF NOT EXISTS heart_beat (
                device_id TEXT,
                factory_id TEXT,
                last_heart_beat TEXT,
                PRIMARY KEY (device_id)
            )
            """
        )

    @staticmethod
    def delete(table_handler: BaseTable, obj):
        table_handler.execute("DELETE FROM heart_beat WHERE device_id=?", (obj.device_id,))

    @staticmethod
    def update(table_handler: BaseTable, obj: HeartBeat, topic: str):
        if DeviceTable.check_device_id(table_handler, topic, obj.device_id):
            time_stamp = str(int(time.time()))
            table_handler.execute(
                """
                INSERT INTO heart_beat (device_id, factory_id, last_heart_beat)
                VALUES (?, ?, ?)
                ON CONFLICT (device_id)
                DO UPDATE SET
                last_heart_beat=?
                """,
                (obj.device_id, obj.factory_id, time_stamp, time_stamp),
            )
            return True
        else:
            if DEBUG:
                Logger.warn(
                    f"device_id - {obj.device_id} is invalid in {topic}, operation was not performed"
                )
            return False

    @staticmethod
    def get_last_time(table_handler: BaseTable, device_id):
        table_handler.query(
            """
            SELECT last_heart_beat
            FROM heart_beat
            WHERE device_id = ?
            """,
            (device_id,),
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res[0][0]
        else:
            return None


class RegisterTable(BaseTable):
    @staticmethod
    def check(table_handler: BaseTable):
        table_handler.execute(
            """
            CREATE TABLE IF NOT EXISTS register (
                device_id TEXT,
                factory_id TEXT,
                device_type TEXT,
                device_position_code TEXT,
                device_position_desc TEXT,
                last_register_timestamp TEXT,
                PRIMARY KEY (device_id)
            )
            """
        )

    def drop(self):
        self.execute("drop table register")

    def delete(self, obj):
        self.execute(
            "DELETE FROM register WHERE device_id=? and factory_id=?",
            (
                obj.device_id,
                obj.factory_id,
            ),
        )

    def insert(self, obj, topic: str):
        if DeviceTable.check_device_id(self, topic, obj.device_id):
            time_stamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
            self.execute(
                """
                INSERT INTO register 
                (device_id, factory_id, device_type, device_position_code, device_position_desc, 
                last_register_timestamp)
                VALUES (?, ?, ?, ?, ?, ?)
                """,
                (
                    obj.device_id,
                    obj.factory_id,
                    obj.device_type,
                    obj.device_position_code,
                    obj.device_position_desc,
                    time_stamp,
                ),
            )
            return True
        else:
            if DEBUG:
                Logger.warn(
                    f"device_id - {obj.device_id} is invalid in {topic}, operation was not performed"
                )
                return False

    @staticmethod
    def update(table_handler, obj: Register, topic: str):
        if DeviceTable.check_device_id(table_handler, topic, obj.device_id):
            time_stamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
            table_handler.execute(
                """
                INSERT INTO register (device_id, factory_id, device_type, device_position_code, device_position_desc, 
                last_register_timestamp)
                VALUES (?, ?, ?, ?, ?, ?)
                ON CONFLICT (device_id)
                DO UPDATE SET
                device_type=?, device_position_code=?, device_position_desc=?, last_register_timestamp=?
                """,
                (
                    obj.device_id,
                    obj.factory_id,
                    obj.device_type,
                    obj.device_position_code,
                    obj.device_position_desc,
                    time_stamp,
                    obj.device_type,
                    obj.device_position_code,
                    obj.device_position_desc,
                    time_stamp,
                ),
            )
            return True
        else:
            if DEBUG:
                Logger.warn(
                    f"device_id - {obj.device_id} is invalid in {topic}, operation was not performed"
                )
            return False

    @staticmethod
    def get_device_position_desc(table_handler: BaseTable, device_id: str):
        """根据device_id获取设备的空间描述信息"""
        table_handler.query(
            """
            SELECT device_position_desc
            FROM register
            WHERE device_id = ?
            """,
            (device_id,),
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res[0][0]
        else:
            return None


class UserInfoTable(BaseTable):
    @staticmethod
    def check(table_handler: BaseTable):
        table_handler.query("SELECT name FROM sqlite_master WHERE type='table' AND name='user_info'")
        if table_handler.cursor.fetchone() is None:
            table_handler.execute(
                f"""
                CREATE TABLE user_info (
                    user_id TEXT,
                    device_id TEXT,
                    name TEXT,
                    user_type INTEGER,
                    qrcode TEXT NULL,
                    face_url TEXT NULL,
                    create_timestamp TEXT,
                    update_timestamp TEXT,
                    PRIMARY KEY (user_id, device_id)
                )
                """
            )
            table_handler.execute(
                f"""
                 CREATE INDEX idx_user_info_qrcode ON user_info(qrcode);
                 """
            )
        init_config_path = os.path.join(os.path.dirname(os.path.abspath("__file__")), "data",
                                        "_users.csv" if ENV_TYPE != 2 else "users.csv")
        if os.path.exists(init_config_path):
            with open(init_config_path, newline='', encoding="utf8") as csvfile:
                csvreader = csv.reader(csvfile)
                head = next(csvreader)
                data = []
                if len(head) == 4:
                    for row in csvreader:
                        user_id = row[0].strip()
                        name = row[1].strip()
                        user_type = 0 if row[2].strip() == "业主" else 1
                        timestamp = str(int(time.time() * 1000))
                        device_id = row[3].strip()
                        data.append((user_id, name, user_type, timestamp, timestamp, device_id))
                    table_handler.executemany(
                        f"""
                            INSERT INTO user_info 
                            (user_id, name, user_type, create_timestamp, update_timestamp, device_id) 
                            VALUES (?, ?, ?, ?, ?, ?) 
                            ON CONFLICT (user_id, device_id) DO NOTHING
                            """,
                        data
                    )
                elif len(head) == 5:
                    for row in csvreader:
                        user_id = row[0].strip()
                        name = row[1].strip()
                        user_type = 0 if row[2].strip() == "业主" else 1
                        timestamp = str(int(time.time() * 1000))
                        device_id = row[3].strip()
                        face_url = row[4].strip()
                        data.append((user_id, name, user_type, timestamp, timestamp, device_id, face_url))
                    table_handler.executemany(
                        f"""
                            INSERT INTO user_info 
                            (user_id, name, user_type, create_timestamp, update_timestamp, device_id, face_url) 
                            VALUES (?, ?, ?, ?, ?, ?, ?) 
                            ON CONFLICT (user_id, device_id) DO NOTHING
                            """,
                        data
                    )

    @staticmethod
    def delete_redundancy_1(table_handler, timestamp):
        """用于在每天清理无效的冗余访客数据"""
        table_handler.execute(f"DELETE FROM user_info WHERE user_type=1 and update_timestamp < {timestamp}")

    @staticmethod
    def delete_redundancy_0(table_handler, timestamp):
        """用于在每天清理无效的冗余业主数据"""
        table_handler.execute(f"DELETE FROM user_info WHERE user_type=0 "
                              f"and (face_url is NULL or face_url = '') and update_timestamp < {timestamp}")

    @staticmethod
    def update(table_handler, obj: UserInfo):
        time_stamp = str(int(time.time() * 1000))
        table_handler.execute(
            """
            INSERT INTO user_info (user_id, name, user_type, create_timestamp, update_timestamp, device_id)
            VALUES (?, ?, ?, ?, ?, ?)
            ON CONFLICT (user_id, device_id)
            DO UPDATE SET
            name=?, update_timestamp=?
            """,
            (obj.user_id, obj.name, obj.user_type, time_stamp, time_stamp, obj.device_id,
             obj.name, time_stamp),
        )

    @staticmethod
    def select_all(table_handler):
        table_handler.execute("SELECT * FROM user_info")
        return table_handler.cursor.fetchall()

    @staticmethod
    def get_name(table_handler, user_id, device_id):
        table_handler.query(
            """
            SELECT name
            FROM user_info
            WHERE user_id = ? AND device_id = ?
            """,
            (user_id, device_id),
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res[0][0]
        else:
            return None

    @staticmethod
    def get_user_by_qrcode(table_handler, qrcode, device_id):
        table_handler.query(
            """
            SELECT user_id, name
            FROM user_info
            WHERE qrcode = ? AND device_id = ?
            """,
            (qrcode, device_id),
        )
        res = table_handler.cursor.fetchall()
        if res:
            return res[0]
        else:
            return None

    @staticmethod
    def update_qrcode(table_handler, user_id, device_id, qrcode):
        time_stamp = str(int(time.time() * 1000))
        table_handler.execute(
            f"""
            UPDATE user_info SET qrcode = ?, update_timestamp = ? WHERE user_id = ? and device_id = ?
            """,
            (qrcode, time_stamp, user_id, device_id)
        )

    @staticmethod
    def update_face_url(table_handler, user_id, device_id, face_url):
        time_stamp = str(int(time.time() * 1000))
        table_handler.execute(
            f"""
            UPDATE user_info SET face_url = ?, update_timestamp = ? WHERE user_id = ? and device_id = ?
            """,
            (face_url, time_stamp, user_id, device_id)
        )

    @staticmethod
    def exists_face_url(table_handler, user_id, device_id):
        """判断人脸地址是否存在且不为空"""
        table_handler.query(
            f"""
            SELECT face_url FROM user_info WHERE user_id = ? AND device_id = ? 
            AND face_url IS NOT NULL AND face_url != ''
            """,
            (user_id, device_id)
        )
        res = table_handler.cursor.fetchall()
        if res and len(res[0]) > 0:
            return True
        else:
            return False


class RecordTable(BaseTable):
    @staticmethod
    def check(table_handler: BaseTable):
        table_handler.execute(
            """
            CREATE TABLE IF NOT EXISTS record (
                user_id TEXT,
                device_id TEXT,
                record_datetime TEXT
            )
            """
        )

    @staticmethod
    def add(table_handler: BaseTable, obj: PushRtAccessRecord):
        table_handler.execute(
            """
            INSERT INTO record (user_id, device_id, record_datetime)
            VALUES (?, ?, ?)
            """,
            (obj.user_id, obj.device_id, str(datetime_to_timestamp(obj.time))),
        )
