"""MSYNC ORM 中转处理器

子服务器端注册 Socket.IO 事件处理器，接收主服务器发来的 ORM 查询请求，
在本地数据库执行 SQL 并返回结果。

用法（子服务器端）:
    from gvsdsdk.msync.orm_handler import ORMRelayHandler

    handler = ORMRelayHandler()
    handler.Register(sio_client)  # 注册到 MSyncSocketClient 实例
"""

import datetime
import logging
import uuid

logger = logging.getLogger(__name__)


class ORMRelayHandler:
    """ORM 中转处理器——在子服务器端执行本地 SQL 查询

    通过 MSYNC Socket.IO 事件协议，主服务器将 ORM 请求转发给子服务器，
    子服务器在本地数据库执行查询后返回结果。
    """

    def Register(self, sio_client):
        """将 ORM 事件处理器注册到 MSyncSocketClient 实例

        Args:
            sio_client: MSyncSocketClient 实例，需要有 _sio 属性
        """
        sio = sio_client._sio

        @sio.on('msync:orm:tables', namespace='/msync')
        def on_orm_tables(data):
            return self.HandleTables(data)

        @sio.on('msync:orm:columns', namespace='/msync')
        def on_orm_columns(data):
            return self.HandleColumns(data)

        @sio.on('msync:orm:list', namespace='/msync')
        def on_orm_list(data):
            return self.HandleList(data)

        @sio.on('msync:orm:add', namespace='/msync')
        def on_orm_add(data):
            return self.HandleAdd(data)

        @sio.on('msync:orm:modify', namespace='/msync')
        def on_orm_modify(data):
            return self.HandleModify(data)

        @sio.on('msync:orm:delete', namespace='/msync')
        def on_orm_delete(data):
            return self.HandleDelete(data)

        logger.info('[ORM] ORM 中转处理器已注册')

    def _GetConnection(self):
        """获取本地数据库连接（Django default）"""
        from django.db import connections
        conn = connections['default']
        conn.ensure_connection()
        return conn

    # ─── 事件处理 ──────────────────────────────────────────────

    def HandleTables(self, data):
        """列出本地数据库中所有用户表"""
        try:
            conn = self._GetConnection()
        except Exception as e:
            return {'code': 1, 'msg': f'数据库连接失败: {e}'}

        try:
            with conn.cursor() as cursor:
                tables = self._ListTables(cursor)
                return {'code': 0, 'msg': 'success', 'data': tables}
        except Exception as e:
            logger.error(f'[ORM] HandleTables 异常: {e}')
            return {'code': 1, 'msg': f'查询失败: {e}'}

    def HandleColumns(self, data):
        """列出指定表的列信息"""
        table_name = (data or {}).get('Table', '')
        if not table_name:
            return {'code': 1, 'msg': '缺少 Table 参数'}

        try:
            conn = self._GetConnection()
        except Exception as e:
            return {'code': 1, 'msg': f'数据库连接失败: {e}'}

        try:
            with conn.cursor() as cursor:
                columns = self._ListColumns(cursor, table_name)
                return {'code': 0, 'msg': 'success', 'data': columns}
        except Exception as e:
            logger.error(f'[ORM] HandleColumns 异常: {e}')
            return {'code': 1, 'msg': f'查询失败: {e}'}

    def HandleList(self, data):
        """分页查询表数据"""
        table_name = (data or {}).get('Table', '')
        if not table_name:
            return {'code': 1, 'msg': '缺少 Table 参数'}

        try:
            conn = self._GetConnection()
        except Exception as e:
            return {'code': 1, 'msg': f'数据库连接失败: {e}'}

        try:
            with conn.cursor() as cursor:
                result = self._QueryTable(cursor, table_name, data or {})
                if result.get('error'):
                    return {'code': 1, 'msg': result['error'], 'data': None}
                return {'code': 0, 'msg': 'success', 'data': result}
        except Exception as e:
            logger.error(f'[ORM] HandleList 异常: {e}')
            return {'code': 1, 'msg': f'查询失败: {e}'}

    def HandleAdd(self, data):
        """新增记录"""
        table_name = (data or {}).get('Table', '')
        record = (data or {}).get('Record', {})
        if not table_name:
            return {'code': 1, 'msg': '缺少 Table 参数'}

        try:
            conn = self._GetConnection()
        except Exception as e:
            return {'code': 1, 'msg': f'数据库连接失败: {e}'}

        try:
            with conn.cursor() as cursor:
                result = self._AddRecord(cursor, table_name, record)
                return result
        except Exception as e:
            logger.error(f'[ORM] HandleAdd 异常: {e}')
            return {'code': 1, 'msg': f'新增失败: {e}'}

    def HandleModify(self, data):
        """修改记录"""
        table_name = (data or {}).get('Table', '')
        record = (data or {}).get('Record', {})
        if not table_name:
            return {'code': 1, 'msg': '缺少 Table 参数'}

        try:
            conn = self._GetConnection()
        except Exception as e:
            return {'code': 1, 'msg': f'数据库连接失败: {e}'}

        try:
            with conn.cursor() as cursor:
                result = self._ModifyRecord(cursor, table_name, record)
                return result
        except Exception as e:
            logger.error(f'[ORM] HandleModify 异常: {e}')
            return {'code': 1, 'msg': f'修改失败: {e}'}

    def HandleDelete(self, data):
        """删除记录"""
        table_name = (data or {}).get('Table', '')
        record = (data or {}).get('Record', {})
        if not table_name:
            return {'code': 1, 'msg': '缺少 Table 参数'}

        try:
            conn = self._GetConnection()
        except Exception as e:
            return {'code': 1, 'msg': f'数据库连接失败: {e}'}

        try:
            with conn.cursor() as cursor:
                result = self._DeleteRecord(cursor, table_name, record)
                return result
        except Exception as e:
            logger.error(f'[ORM] HandleDelete 异常: {e}')
            return {'code': 1, 'msg': f'删除失败: {e}'}

    # ─── SQL 执行 ──────────────────────────────────────────────

    def _ListTables(self, cursor):
        """列出所有用户表，含 i18n 标题"""
        cursor.execute(
            "SELECT TABLE_NAME, TABLE_ROWS, TABLE_COMMENT FROM information_schema.TABLES "
            "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE' "
            "ORDER BY TABLE_NAME"
        )

        from django.apps import apps
        # 构建表名→中文标题映射
        model_verbose = {}
        for model in apps.get_models():
            meta = model._meta
            # 优先使用 table_title（自定义 Meta 属性），其次 verbose_name
            table_title = getattr(meta, 'table_title', None)
            title = str(table_title) if table_title else (str(meta.verbose_name) if meta.verbose_name else '')
            if title:
                model_verbose[meta.object_name] = title
                model_verbose[meta.db_table] = title

        result = []
        for row in cursor.fetchall():
            table_name, table_rows, table_comment = row
            # 优先级：TABLE_COMMENT > table_title/verbose_name > 表名
            title = table_comment or model_verbose.get(table_name) or table_name
            result.append({'name': table_name, 'title': title, 'count': table_rows or 0})
        return result

    def _ListColumns(self, cursor, table_name):
        """列出指定表的列信息"""
        cursor.execute(
            "SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_KEY, EXTRA, COLUMN_TYPE, COLUMN_COMMENT "
            "FROM information_schema.COLUMNS "
            "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s "
            "ORDER BY ORDINAL_POSITION",
            [table_name]
        )

        model_verbose = self._FindModelVerboseNames(table_name)
        logger.info(f'[ORM] _ListColumns table={table_name} model_verbose={model_verbose}')

        columns = []
        for row in cursor.fetchall():
            col_name, data_type, is_nullable, col_key, extra, col_type, col_comment = row
            is_pk = col_key == 'PRI'
            is_auto = 'auto_increment' in (extra or '').lower()

            if data_type in ('int', 'bigint', 'tinyint', 'smallint', 'mediumint'):
                ftype = 'number'
            elif data_type in ('float', 'double', 'decimal'):
                ftype = 'number'
            elif data_type in ('datetime', 'timestamp'):
                ftype = 'datetime'
            elif data_type in ('date',):
                ftype = 'date'
            elif data_type in ('time',):
                ftype = 'time'
            elif data_type in ('json',):
                ftype = 'textarea'
            elif data_type in ('text', 'mediumtext', 'longtext', 'tinytext'):
                ftype = 'textarea'
            elif data_type in ('binary', 'varbinary', 'blob'):
                ftype = 'uuid'
            else:
                ftype = 'text'

            title = col_comment or model_verbose.get(col_name) or col_name

            columns.append({
                'field': col_name,
                'title': title,
                'type': ftype,
                'editable': not is_pk and not is_auto,
                'sortable': True,
                'filterable': True,
                'primaryKey': is_pk,
                'hideInForm': is_pk,
                'required': is_nullable == 'NO' and not is_auto,
                'dbType': col_type,
            })
        return columns

    def _QueryTable(self, cursor, table_name, params):
        """分页查询表数据"""
        cursor.execute(
            "SELECT COUNT(*) FROM information_schema.TABLES "
            "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s",
            [table_name]
        )
        if cursor.fetchone()[0] == 0:
            return {'records': [], 'total': 0, 'error': f'表不存在: {table_name}'}

        col_info = self._GetTableColumns(cursor, table_name)
        pk_cols = {k: v for k, v in col_info.items() if v['is_pk']}

        where_parts = []
        where_values = []
        for pk_name in pk_cols:
            pk_val = params.get(pk_name, '')
            if pk_val:
                where_parts.append(f'`{pk_name}` = %s')
                where_values.append(self._ConvertValueForDB(pk_val, pk_cols[pk_name]))

        keyword = params.get('keyword', '')
        if not where_parts and keyword:
            cursor.execute(
                "SELECT COLUMN_NAME FROM information_schema.COLUMNS "
                "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s "
                "AND DATA_TYPE IN ('varchar', 'char', 'text', 'mediumtext', 'longtext')",
                [table_name]
            )
            text_cols = [row[0] for row in cursor.fetchall()]
            if text_cols:
                conditions = [f'`{c}` LIKE %s' for c in text_cols]
                or_clause = ' OR '.join(conditions)
                where_parts.append(f'({or_clause})')
                where_values.extend([f'%{keyword}%'] * len(text_cols))

        where_clause = (' WHERE ' + ' AND '.join(where_parts)) if where_parts else ''

        if where_clause:
            cursor.execute(f'SELECT COUNT(*) FROM `{table_name}`{where_clause}', where_values)
            total = cursor.fetchone()[0]
        else:
            cursor.execute(f'SELECT COUNT(*) FROM `{table_name}`')
            total = cursor.fetchone()[0]

        try:
            page = max(int(params.get('PageIndex', 1)), 1)
        except (ValueError, TypeError):
            page = 1
        try:
            page_size = min(max(int(params.get('PageSize', 20)), 1), 200)
        except (ValueError, TypeError):
            page_size = 20

        offset = (page - 1) * page_size
        cursor.execute(
            f'SELECT * FROM `{table_name}`{where_clause} ORDER BY 1 LIMIT %s OFFSET %s',
            where_values + [page_size, offset]
        )
        desc = [d[0] for d in cursor.description]
        records = []
        for row in cursor.fetchall():
            record = {}
            for i, val in enumerate(row):
                val = self._SerializeValue(val)
                record[desc[i]] = val
            records.append(record)

        return {'records': records, 'total': total, 'page': page, 'PageSize': page_size}

    def _AddRecord(self, cursor, table_name, record):
        """新增记录"""
        if not self._ValidateTableName(cursor, table_name):
            return {'code': 1, 'msg': f'表 {table_name} 不存在'}

        col_info = self._GetTableColumns(cursor, table_name)

        fields = []
        values = []
        for key, val in record.items():
            if key not in col_info:
                continue
            if col_info[key]['is_auto']:
                continue
            fields.append(f'`{key}`')
            values.append(self._ConvertValueForDB(val, col_info[key]))

        if not fields:
            return {'code': 1, 'msg': '没有可插入的字段'}

        placeholders = ', '.join(['%s'] * len(fields))
        sql = f'INSERT INTO `{table_name}` ({", ".join(fields)}) VALUES ({placeholders})'
        cursor.execute(sql, values)
        return {'code': 0, 'msg': '新增成功'}

    def _ModifyRecord(self, cursor, table_name, record):
        """修改记录"""
        if not self._ValidateTableName(cursor, table_name):
            return {'code': 1, 'msg': f'表 {table_name} 不存在'}

        col_info = self._GetTableColumns(cursor, table_name)
        pk_cols = {k: v for k, v in col_info.items() if v['is_pk']}

        if not pk_cols:
            return {'code': 1, 'msg': '表没有主键，无法定位记录'}

        set_parts = []
        set_values = []
        for key, val in record.items():
            if key not in col_info or key in pk_cols:
                continue
            set_parts.append(f'`{key}` = %s')
            set_values.append(self._ConvertValueForDB(val, col_info[key]))

        if not set_parts:
            return {'code': 0, 'msg': '没有需要更新的字段'}

        where_parts = []
        where_values = []
        for pk_name in pk_cols:
            if pk_name not in record:
                return {'code': 1, 'msg': f'缺少主键字段: {pk_name}'}
            where_parts.append(f'`{pk_name}` = %s')
            where_values.append(self._ConvertValueForDB(record[pk_name], pk_cols[pk_name]))

        sql = f'UPDATE `{table_name}` SET {", ".join(set_parts)} WHERE {" AND ".join(where_parts)}'
        cursor.execute(sql, set_values + where_values)

        if cursor.rowcount == 0:
            return {'code': 1, 'msg': '记录不存在或未变更'}
        return {'code': 0, 'msg': '修改成功'}

    def _DeleteRecord(self, cursor, table_name, record):
        """删除记录"""
        if not self._ValidateTableName(cursor, table_name):
            return {'code': 1, 'msg': f'表 {table_name} 不存在'}

        col_info = self._GetTableColumns(cursor, table_name)
        pk_cols = {k: v for k, v in col_info.items() if v['is_pk']}

        if not pk_cols:
            return {'code': 1, 'msg': '表没有主键，无法定位记录'}

        where_parts = []
        where_values = []
        for pk_name in pk_cols:
            if pk_name not in record:
                return {'code': 1, 'msg': f'缺少主键字段: {pk_name}'}
            where_parts.append(f'`{pk_name}` = %s')
            where_values.append(self._ConvertValueForDB(record[pk_name], pk_cols[pk_name]))

        sql = f'DELETE FROM `{table_name}` WHERE {" AND ".join(where_parts)}'
        cursor.execute(sql, where_values)

        if cursor.rowcount == 0:
            return {'code': 1, 'msg': '记录不存在'}
        return {'code': 0, 'msg': '删除成功'}

    # ─── 工具方法 ──────────────────────────────────────────────

    def _ValidateTableName(self, cursor, table_name):
        """验证表名存在"""
        cursor.execute(
            "SELECT COUNT(*) FROM information_schema.TABLES "
            "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s",
            [table_name]
        )
        return cursor.fetchone()[0] > 0

    def _GetTableColumns(self, cursor, table_name):
        """获取表的列信息"""
        cursor.execute(
            "SELECT COLUMN_NAME, DATA_TYPE, COLUMN_TYPE, COLUMN_KEY, EXTRA, IS_NULLABLE "
            "FROM information_schema.COLUMNS "
            "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s "
            "ORDER BY ORDINAL_POSITION",
            [table_name]
        )
        cols = {}
        for row in cursor.fetchall():
            col_name, data_type, column_type, col_key, extra, is_nullable = row
            cols[col_name] = {
                'type': data_type,
                'column_type': column_type or '',
                'is_pk': col_key == 'PRI',
                'is_auto': 'auto_increment' in (extra or '').lower(),
                'is_nullable': is_nullable == 'YES',
            }
        return cols

    def _ConvertValueForDB(self, value, col_info):
        """将前端传来的值转换为数据库可接受的类型"""
        if value is None or value == '':
            if col_info['is_nullable']:
                return None
            return ''

        data_type = col_info['type']

        if data_type in ('int', 'bigint', 'tinyint', 'smallint', 'mediumint'):
            return int(value)
        elif data_type in ('float', 'double', 'decimal'):
            return float(value)
        elif data_type in ('binary', 'varbinary'):
            import re
            hex_str = re.sub(r'[^0-9a-fA-F]', '', str(value))
            if len(hex_str) == 32:
                return uuid.UUID(hex_str).bytes
            return value

        return value

    def _SerializeValue(self, val):
        """将数据库值序列化为 JSON 安全格式"""
        if isinstance(val, bytes):
            if len(val) == 16:
                try:
                    return str(uuid.UUID(bytes=val))
                except ValueError:
                    return val.hex()
            return val.hex()
        elif isinstance(val, datetime.datetime):
            return val.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(val, datetime.date):
            return val.isoformat()
        elif isinstance(val, datetime.time):
            return val.strftime('%H:%M:%S')
        elif isinstance(val, (bytearray, memoryview)):
            return bytes(val).hex()
        return val

    def _FindModelVerboseNames(self, table_name):
        """从 Django 模型中获取字段的 verbose_name"""
        from django.apps import apps
        verbose_map = {}
        try:
            for model in apps.get_models():
                if model._meta.db_table == table_name:
                    logger.info(f'[ORM] _FindModelVerboseNames matched model={model.__name__} db_table={model._meta.db_table}')
                    for f in model._meta.get_fields():
                        # 获取数据库列名
                        col_name = None
                        if hasattr(f, 'column') and f.column:
                            col_name = f.column
                        elif hasattr(f, 'db_column') and f.db_column:
                            col_name = f.db_column
                        elif hasattr(f, 'attname'):
                            col_name = f.attname

                        if not col_name:
                            continue

                        vn = getattr(f, 'verbose_name', None)
                        if vn and str(vn) != col_name:
                            verbose_map[col_name] = str(vn)
                            logger.debug(f'[ORM]   {col_name} -> {str(vn)}')
                    break
            else:
                # 没有匹配的模型
                available = [m._meta.db_table for m in apps.get_models()]
                logger.warning(f'[ORM] _FindModelVerboseNames: 未找到 db_table={table_name} 的模型, 可用表: {available[:20]}...')
        except Exception as e:
            logger.error(f'[ORM] _FindModelVerboseNames 异常: {e}')
        return verbose_map
