"""
Apache Doris 数据库优化模块
针对 Doris 特性的优化策略：
1. 连接池配置优化
2. 批量操作优化
3. 查询性能监控
4. 表结构优化建议
"""
import os
import time
import pymysql
from contextlib import contextmanager
from collections import deque
from threading import Lock
from lib.logger import log_info, log_error, log_warning


class DorisConnectionPool:
    """Doris 数据库连接池"""
    
    _instance = None
    _lock = Lock()
    
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance._initialized = False
        return cls._instance
    
    def __init__(self, host=None, port=None, user=None, password=None, 
                 database=None, pool_size=10, **kwargs):
        if self._initialized:
            return
            
        self._host = host or os.environ.get('DB_HOST', 'haoslm2.xicp.net')
        self._port = port or int(os.environ.get('DB_PORT', 10216))
        self._user = user or os.environ.get('DB_USER', 'root')
        self._password = password or os.environ.get('DB_PASSWORD', 'DsideaL147258369')
        self._database = database or os.environ.get('DB_NAME', 'yltcharge')
        self._pool_size = pool_size
        
        # 连接池
        self._pool = deque(maxlen=pool_size)
        self._pool_lock = Lock()
        self._total_connections = 0
        self._initialized = True
        
        # 预热连接池
        self._preheat_pool()
        log_info(f"[Doris连接池] 初始化完成，目标连接数: {pool_size}", 'doris')
    
    def _preheat_pool(self):
        """预热连接池"""
        try:
            for i in range(min(3, self._pool_size)):
                try:
                    conn = self._create_connection()
                    with self._pool_lock:
                        self._pool.append(conn)
                        self._total_connections += 1
                    log_info(f"[Doris连接池] 预热连接 {i+1}/3 成功", 'doris')
                except Exception as e:
                    log_warning(f"[Doris连接池] 预热连接失败: {e}", 'doris')
        except Exception as e:
            log_warning(f"[Doris连接池] 预热异常: {e}", 'doris')
    
    def _create_connection(self):
        """创建新连接"""
        conn = pymysql.connect(
            host=self._host,
            port=self._port,
            user=self._user,
            password=self._password,
            database=self._database,
            charset='utf8mb4',
            cursorclass=pymysql.cursors.DictCursor,
            autocommit=True,
            connect_timeout=10,  # 缩短连接超时
            read_timeout=120,
            write_timeout=120,
        )
        return conn
    
    def get_connection(self, timeout=5):
        """获取连接（带超时）"""
        start_time = time.time()
        
        # 先从池中获取
        with self._pool_lock:
            if self._pool:
                conn = self._pool.pop()
                # 检查连接是否有效
                try:
                    conn.ping(reconnect=False)
                    return conn
                except Exception:
                    # 连接失效，创建新连接
                    self._total_connections -= 1
                    try:
                        conn.close()
                    except Exception:
                        pass
        
        # 池没有可用连接，等待或创建新连接
        elapsed = time.time() - start_time
        if elapsed < timeout:
            # 尝试创建新连接（如果总数未超过限制）
            if self._total_connections < self._pool_size:
                try:
                    conn = self._create_connection()
                    with self._pool_lock:
                        self._total_connections += 1
                    return conn
                except Exception as e:
                    log_warning(f"[Doris连接池] 创建新连接失败: {e}", 'doris')
        
        # 超时或达到上限，强制创建新连接
        log_warning(f"[Doris连接池] 强制创建新连接（池已满或超时）", 'doris')
        return self._create_connection()
    
    def return_connection(self, conn):
        """归还连接到池"""
        if conn is None:
            return
        try:
            conn.ping(reconnect=False)
            with self._pool_lock:
                if len(self._pool) < self._pool_size:
                    self._pool.append(conn)
                    return
        except Exception:
            pass
        
        # 连接失效或池已满，关闭连接
        try:
            conn.close()
        except Exception:
            pass
        with self._pool_lock:
            self._total_connections = max(0, self._total_connections - 1)
    
    def get_stats(self):
        """获取连接池状态"""
        with self._pool_lock:
            return {
                'pool_size': len(self._pool),
                'total_connections': self._total_connections,
                'max_pool_size': self._pool_size
            }


# 全局连接池实例
_pool = None


def get_pool():
    """获取全局连接池"""
    global _pool
    if _pool is None:
        _pool = DorisConnectionPool(pool_size=10)
    return _pool


@contextmanager
def get_doris_connection():
    """获取 Doris 连接的上下文管理器"""
    pool = get_pool()
    conn = pool.get_connection(timeout=3)
    try:
        yield conn
    except pymysql.Error as e:
        log_error(f"[Doris] 数据库操作失败: {e}", 'doris')
        # 连接可能已失效，不归还到池
        try:
            conn.close()
        except Exception:
            pass
        with pool._pool_lock:
            pool._total_connections = max(0, pool._total_connections - 1)
        raise
    else:
        pool.return_connection(conn)


def execute_doris_query(sql, params=None, retry=2):
    """
    执行 Doris 查询（带重试和性能监控）
    
    Args:
        sql: SQL 语句
        params: 参数
        retry: 重试次数
    
    Returns:
        list: 查询结果
    """
    for attempt in range(retry + 1):
        try:
            start_time = time.time()
            
            with get_doris_connection() as conn:
                with conn.cursor() as cursor:
                    cursor.execute(sql, params)
                    result = cursor.fetchall()
            
            elapsed = (time.time() - start_time) * 1000  # 毫秒
            
            # 慢查询监控（超过100ms记录）
            if elapsed > 100:
                log_info(f"[Doris慢查询] 耗时: {elapsed:.1f}ms, SQL: {sql[:200]}, 行数: {len(result) if result else 0}", 'doris')
            
            if attempt > 0:
                log_info(f'[Doris] 查询重试成功，第{attempt+1}次尝试', 'doris')
            
            return result
            
        except pymysql.err.OperationalError as e:
            if attempt < retry and (e.args[0] == 2013 or e.args[0] == 2006):
                log_warning(f'[Doris] 查询连接断开，正在重试（第{attempt+1}次）: {e}', 'doris')
                time.sleep(0.5 * (attempt + 1))
                continue
            log_error(f'[Doris] 查询失败: {e}\nSQL: {sql}\nParams: {params}', 'doris')
            raise
        except Exception as e:
            log_error(f'[Doris] 查询失败: {e}\nSQL: {sql}\nParams: {params}', 'doris')
            raise


def execute_doris_batch_insert(sql, params_list, batch_size=100, retry=2):
    """
    Doris 批量插入优化
    
    Args:
        sql: SQL 语句（带 %s 占位符）
        params_list: 参数列表
        batch_size: 每批大小
        retry: 重试次数
    
    Returns:
        int: 插入的总行数
    """
    if not params_list:
        return 0
    
    total_inserted = 0
    start_time = time.time()
    
    for i in range(0, len(params_list), batch_size):
        batch = params_list[i:i + batch_size]
        batch_sql = sql.rstrip()
        
        # 对于 Doris，可以使用 INSERT INTO ... VALUES (...), (...), (...) 格式
        # 但 pymysql 的 executemany 已经处理好了
        
        for attempt in range(retry + 1):
            try:
                with get_doris_connection() as conn:
                    with conn.cursor() as cursor:
                        cursor.executemany(batch_sql, batch)
                        total_inserted += len(batch)
                
                if attempt > 0:
                    log_info(f'[Doris] 批量插入重试成功，第{attempt+1}次尝试', 'doris')
                break
                
            except pymysql.err.OperationalError as e:
                if attempt < retry and (e.args[0] == 2013 or e.args[0] == 2006):
                    log_warning(f'[Doris] 批量插入连接断开，正在重试（第{attempt+1}次）: {e}', 'doris')
                    time.sleep(0.5 * (attempt + 1))
                    continue
                log_error(f'[Doris] 批量插入失败: {e}\nSQL: {batch_sql}', 'doris')
                raise
            except Exception as e:
                log_error(f'[Doris] 批量插入失败: {e}\nSQL: {batch_sql}', 'doris')
                raise
    
    elapsed = (time.time() - start_time) * 1000
    log_info(f'[Doris] 批量插入完成，共 {total_inserted} 行，耗时 {elapsed:.1f}ms', 'doris')
    
    return total_inserted


def execute_doris_update(sql, params=None, retry=2):
    """
    执行 Doris 更新操作（带重试）
    
    Args:
        sql: SQL 语句
        params: 参数
        retry: 重试次数
    
    Returns:
        int: 影响的行数
    """
    for attempt in range(retry + 1):
        try:
            start_time = time.time()
            
            with get_doris_connection() as conn:
                with conn.cursor() as cursor:
                    affected = cursor.execute(sql, params)
            
            elapsed = (time.time() - start_time) * 1000
            
            if elapsed > 100:
                log_info(f"[Doris慢更新] 耗时: {elapsed:.1f}ms, SQL: {sql[:200]}", 'doris')
            
            if attempt > 0:
                log_info(f'[Doris] 更新重试成功，第{attempt+1}次尝试', 'doris')
            
            return cursor.rowcount if hasattr(cursor, 'rowcount') else affected
            
        except pymysql.err.OperationalError as e:
            if attempt < retry and (e.args[0] == 2013 or e.args[0] == 2006):
                log_warning(f'[Doris] 更新连接断开，正在重试（第{attempt+1}次）: {e}', 'doris')
                time.sleep(0.5 * (attempt + 1))
                continue
            log_error(f'[Doris] 更新失败: {e}\nSQL: {sql}\nParams: {params}', 'doris')
            raise
        except Exception as e:
            log_error(f'[Doris] 更新失败: {e}\nSQL: {sql}\nParams: {params}', 'doris')
            raise


def optimize_doris_table(table_name):
    """
    优化 Doris 表的统计信息
    
    Args:
        table_name: 表名
    """
    try:
        sql = f"ANALYZE TABLE {table_name} UPDATE HISTOGRAM"
        execute_doris_update(sql)
        log_info(f"[Doris] 表 {table_name} 统计信息已更新", 'doris')
        return True
    except Exception as e:
        log_warning(f"[Doris] 表 {table_name} 统计信息更新失败: {e}", 'doris')
        # 尝试使用 COMPUTE STATISTICS
        try:
            sql = f"ANALYZE TABLE {table_name} COMPUTE STATISTICS"
            execute_doris_update(sql)
            log_info(f"[Doris] 表 {table_name} 统计信息已计算", 'doris')
            return True
        except Exception as e2:
            log_warning(f"[Doris] 表 {table_name} 统计信息计算也失败: {e2}", 'doris')
            return False


def get_doris_table_stats(table_name):
    """
    获取 Doris 表的统计信息
    
    Args:
        table_name: 表名
    
    Returns:
        dict: 统计信息
    """
    try:
        sql = f"SHOW TABLE STATUS LIKE '{table_name}'"
        result = execute_doris_query(sql)
        if result:
            return result[0]
        return {}
    except Exception as e:
        log_warning(f"[Doris] 获取表 {table_name} 统计信息失败: {e}", 'doris')
        return {}


def print_pool_stats():
    """打印连接池状态"""
    pool = get_pool()
    stats = pool.get_stats()
    log_info(f"[Doris连接池] 状态: {stats}", 'doris')
    return stats
