# -*- coding: utf-8 -*-
from datetime import datetime
import sqlite3

from ddans.native.system import NSystem
from ddans.native.config import NConfig
from ddans.native.hook import NHook
from ddans.native.log import NLog


class DBModel:
    _fields = {}

    @classmethod
    def create_table(cls):
        dbfilename = cls.dbfilename()
        NSystem.ensure_dir(dbfilename)

        existing_columns = cls.table_columns()
        if not existing_columns:
            cls._create_table()
            return

        # Add missing columns
        for name, field in cls._fields.items():
            if name not in existing_columns:
                sql = (f"ALTER TABLE {cls.__name__.lower()} ADD COLUMN "
                       f"{name} {field.field_type}")
                if field.default is not None:
                    sql += f" DEFAULT {field.default}"
                cls._execute(sql)

    @classmethod
    def _create_table(cls):
        fields = []
        for name, field in cls._fields.items():
            field_def = f"{name} {field.field_type}"
            if field.primary_key:
                field_def += " PRIMARY KEY"
                if field.autoincrement:
                    field_def += " AUTOINCREMENT"
            if not field.nullable:
                field_def += " NOT NULL"
            if field.default is not None:
                field_def += f" DEFAULT {field.default}"
            fields.append(field_def)
        sql = (f"CREATE TABLE IF NOT EXISTS {cls.__name__.lower()} "
               f"({', '.join(fields)})")
        NSystem.ensure_dir(cls.dbfilename())
        cls._execute(sql)

    @classmethod
    def table_columns(cls):
        sql = f"PRAGMA table_info({cls.__name__.lower()})"
        rows = cls._execute(sql, callback=lambda cur: cur.fetchall())
        if rows:
            columns = [column[1] for column in rows]
            return columns
        else:
            return None

    @classmethod
    def dbfilename(cls, name="ddan"):
        return NSystem.join(NConfig.app_data("base"), f"{name}.db")

    @classmethod
    def sqlSelect(cls, select="*", where=None):
        where
        if NHook.isvalid_str(where):
            sql = f"SELECT {select} FROM {cls.__name__.lower()} WHERE {where}"
        else:
            sql = f"SELECT {select} FROM {cls.__name__.lower()}"
        return sql

    @classmethod
    def _execute(cls, sql: str, params=None, callback=None):
        result = None
        if not NHook.isvalid_str(sql):
            return result

        with sqlite3.connect(cls.dbfilename()) as conn:
            cur = conn.cursor()
            try:
                if params:
                    cur.execute(sql, params)
                else:
                    cur.execute(sql)
                if callback:
                    result = callback(cur)
                conn.commit()
                return result
            except sqlite3.Error as e:
                NLog.error(f"execute: {sql}\n{e}")
                conn.rollback()
                return None
            except Exception:
                conn.rollback()
                return None
                # raise e

    @classmethod
    def from_row(cls, row):
        obj = cls()
        for (name, field), value in zip(cls._fields.items(), row):
            setattr(obj, name, value)
        return obj

    @classmethod
    def get_by_id(cls, id):
        if not NHook.isvalid(id):
            return None
        sql = cls.sqlSelect(where="id = ?")

        def callback(cur):
            return cur.fetchone()

        row = cls._execute(sql, (id, ), callback=callback)
        if row:
            return cls.from_row(row)
        return None

    @classmethod
    def get_by_rid(cls, rid):
        if not NHook.isvalid_str(rid):
            return None
        sql = cls.sqlSelect(where="rid = ?")
        row = cls._execute(sql, (rid, ), callback=lambda cur: cur.fetchone())
        if row:
            return cls.from_row(row)
        return None

    @classmethod
    def get(cls, conditions: dict = {}):
        # # 构建条件语句
        # where_conditions = []
        # parameters = []
        # for key, value in conditions.items():
        #     where_conditions.append(f"{key} = ?")
        #     parameters.append(value)

        condition_strings = [f"{key} = ?" for key in conditions.keys()]
        condition_clause = " AND ".join(condition_strings)
        sql = cls.sqlSelect(where=condition_clause)

        params = tuple(conditions.values())
        rows = cls._execute(sql, params, callback=lambda cur: cur.fetchall())
        if rows:
            return [cls.from_row(row) for row in rows]
        return None

    @classmethod
    def query(cls, fields, conditions=None):
        if not NHook.isvalid(fields):
            return None
        field_clause = ', '.join(fields)
        condition_clause = ""
        params = ()
        if conditions:
            condition_strings = [f"{key} = ?" for key in conditions.keys()]
            condition_clause = " WHERE " + " AND ".join(condition_strings)
            params = tuple(conditions.values())

        sql = cls.sqlSelect(field_clause, where=condition_clause)
        rows = cls._execute(sql, params, callback=lambda cur: cur.fetchall())
        if rows:
            return [dict(zip(fields, row)) for row in rows]
        return None

    def save(self):
        if self._exists():
            self._update()
        else:
            self._insert()

    def _insert(self):
        current_time = datetime.now().isoformat(timespec='seconds')
        fields = ', '.join(
            name for name in self._fields.keys()
            if not (self._fields[name].primary_key and self._fields[name].
                    autoincrement) and name not in ['created', 'updated'])
        placeholders = ', '.join(
            '?' for name in self._fields.keys()
            if not (self._fields[name].primary_key and self._fields[name].
                    autoincrement) and name not in ['created', 'updated'])
        values = [
            getattr(self, name) for name in self._fields.keys()
            if not (self._fields[name].primary_key and self._fields[name].
                    autoincrement) and name not in ['created', 'updated']
        ]

        values.append(current_time)  # created
        values.append(current_time)  # updated

        sql = (f"INSERT INTO {self.__class__.__name__.lower()} "
               f"({fields}, created, updated) VALUES ({placeholders}, ?, ?)")
        self.__class__._execute(sql, values)

    def _update(self):
        current_time = datetime.now().isoformat(timespec='seconds')
        fields = ', '.join(f"{name} = ?" for name in self._fields.keys()
                           if name not in ['id', 'created', 'updated'])
        values = [
            getattr(self, name) for name in self._fields.keys()
            if name not in ['id', 'created', 'updated']
        ]
        values.append(current_time)
        values.append(self.id)  # Assuming 'id' is the primary key

        sql = (f"UPDATE {self.__class__.__name__.lower()} SET {fields}, "
               f"updated = ? WHERE id = ?")
        self.__class__._execute(sql, values)

    def _exists(self):
        if not hasattr(self, 'id'):
            return False
        sql = self.__class__.sqlSelect(where="id = ?")
        row = self.__class__._execute(sql, (self.id, ),
                                      callback=lambda cur: cur.fetchone())
        return row is not None
