package com.ruoyi.framework.util;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ruoyi.framework.domain.DepartmentDataPerformanceMetrics;
import com.ruoyi.framework.security.context.DepartmentContextHolder;

/**
 * 部门数据隔离性能监控工具类
 * 
 * @author ruoyi
 * @date 2025-01-12
 */
public class DepartmentDataPerformanceUtil
{
    private static final Logger log = LoggerFactory.getLogger(DepartmentDataPerformanceUtil.class);

    /**
     * SQL语句截取长度
     */
    private static final int SQL_TRUNCATE_LENGTH = 500;

    /**
     * 表名提取正则表达式
     */
    private static final Pattern TABLE_NAME_PATTERN = Pattern.compile(
        "(?:from|join|update|into)\\s+([a-zA-Z_][a-zA-Z0-9_]*)", 
        Pattern.CASE_INSENSITIVE
    );

    /**
     * 创建性能监控指标对象
     * 
     * @param tableName 表名
     * @param operationType 操作类型
     * @param executionTime 执行时间(毫秒)
     * @param sql SQL语句
     * @return 性能监控指标对象
     */
    public static DepartmentDataPerformanceMetrics createMetrics(String tableName, String operationType, 
                                                               long executionTime, String sql)
    {
        DepartmentDataPerformanceMetrics metrics = new DepartmentDataPerformanceMetrics();
        
        metrics.setMonitorTime(new Date());
        metrics.setTableName(tableName);
        metrics.setOperationType(operationType);
        metrics.setExecutionTime(BigDecimal.valueOf(executionTime));
        metrics.setDeptId(DepartmentContextHolder.getCurrentDeptId());
        metrics.setUsername(DepartmentContextHolder.getCurrentUsername());
        metrics.setIsSuperAdmin(DepartmentContextHolder.isSuperAdmin());
        
        // 截取SQL语句
        if (sql != null && sql.length() > SQL_TRUNCATE_LENGTH)
        {
            metrics.setSqlStatement(sql.substring(0, SQL_TRUNCATE_LENGTH) + "...");
        }
        else
        {
            metrics.setSqlStatement(sql);
        }
        
        return metrics;
    }

    /**
     * 从SQL语句中提取表名
     * 
     * @param sql SQL语句
     * @return 表名，如果未找到则返回null
     */
    public static String extractTableName(String sql)
    {
        if (sql == null || sql.trim().isEmpty())
        {
            return null;
        }
        
        Matcher matcher = TABLE_NAME_PATTERN.matcher(sql);
        if (matcher.find())
        {
            return matcher.group(1);
        }
        
        return null;
    }

    /**
     * 检查SQL是否使用了部门索引
     * 
     * @param conn 数据库连接
     * @param sql SQL语句
     * @param tableName 表名
     * @return 是否使用了部门索引
     */
    public static boolean checkDepartmentIndexUsage(Connection conn, String sql, String tableName)
    {
        if (conn == null || sql == null || tableName == null)
        {
            return false;
        }
        
        try
        {
            String explainSql = "EXPLAIN " + sql;
            
            try (PreparedStatement stmt = conn.prepareStatement(explainSql);
                 ResultSet rs = stmt.executeQuery())
            {
                while (rs.next())
                {
                    String table = rs.getString("table");
                    String key = rs.getString("key");
                    
                    if (tableName.equals(table) && key != null)
                    {
                        // 检查是否使用了部门相关的索引
                        if (key.toLowerCase().contains("dept") || 
                            key.startsWith("idx_" + tableName.substring(3) + "_dept"))
                        {
                            return true;
                        }
                    }
                }
            }
        }
        catch (SQLException e)
        {
            log.debug("检查部门索引使用情况时发生异常: {}", e.getMessage());
        }
        
        return false;
    }

    /**
     * 获取查询扫描的行数
     * 
     * @param conn 数据库连接
     * @param sql SQL语句
     * @return 扫描的行数，如果无法获取则返回null
     */
    public static Long getRowsScanned(Connection conn, String sql)
    {
        if (conn == null || sql == null)
        {
            return null;
        }
        
        try
        {
            String explainSql = "EXPLAIN " + sql;
            
            try (PreparedStatement stmt = conn.prepareStatement(explainSql);
                 ResultSet rs = stmt.executeQuery())
            {
                long totalRows = 0;
                
                while (rs.next())
                {
                    Object rowsObj = rs.getObject("rows");
                    if (rowsObj != null)
                    {
                        totalRows += Long.parseLong(rowsObj.toString());
                    }
                }
                
                return totalRows > 0 ? totalRows : null;
            }
        }
        catch (SQLException | NumberFormatException e)
        {
            log.debug("获取查询扫描行数时发生异常: {}", e.getMessage());
        }
        
        return null;
    }

    /**
     * 获取查询计划成本
     * 
     * @param conn 数据库连接
     * @param sql SQL语句
     * @return 查询计划成本，如果无法获取则返回null
     */
    public static BigDecimal getQueryCost(Connection conn, String sql)
    {
        if (conn == null || sql == null)
        {
            return null;
        }
        
        try
        {
            // 使用 EXPLAIN FORMAT=JSON 获取更详细的成本信息
            String explainSql = "EXPLAIN FORMAT=JSON " + sql;
            
            try (PreparedStatement stmt = conn.prepareStatement(explainSql);
                 ResultSet rs = stmt.executeQuery())
            {
                if (rs.next())
                {
                    String jsonResult = rs.getString(1);
                    // 这里可以解析JSON获取成本信息
                    // 由于复杂性，这里返回一个估算值
                    return BigDecimal.valueOf(1.0);
                }
            }
        }
        catch (SQLException e)
        {
            log.debug("获取查询计划成本时发生异常: {}", e.getMessage());
        }
        
        return null;
    }

    /**
     * 获取使用的索引名称
     * 
     * @param conn 数据库连接
     * @param sql SQL语句
     * @param tableName 表名
     * @return 索引名称，如果未使用索引则返回null
     */
    public static String getUsedIndexName(Connection conn, String sql, String tableName)
    {
        if (conn == null || sql == null || tableName == null)
        {
            return null;
        }
        
        try
        {
            String explainSql = "EXPLAIN " + sql;
            
            try (PreparedStatement stmt = conn.prepareStatement(explainSql);
                 ResultSet rs = stmt.executeQuery())
            {
                while (rs.next())
                {
                    String table = rs.getString("table");
                    String key = rs.getString("key");
                    
                    if (tableName.equals(table) && key != null && !"PRIMARY".equals(key))
                    {
                        return key;
                    }
                }
            }
        }
        catch (SQLException e)
        {
            log.debug("获取使用的索引名称时发生异常: {}", e.getMessage());
        }
        
        return null;
    }

    /**
     * 判断是否为慢查询
     * 
     * @param executionTime 执行时间(毫秒)
     * @param threshold 慢查询阈值(毫秒)，默认1000ms
     * @return 是否为慢查询
     */
    public static boolean isSlowQuery(long executionTime, long threshold)
    {
        return executionTime > (threshold > 0 ? threshold : 1000);
    }

    /**
     * 判断是否为慢查询（使用默认阈值1000ms）
     * 
     * @param executionTime 执行时间(毫秒)
     * @return 是否为慢查询
     */
    public static boolean isSlowQuery(long executionTime)
    {
        return isSlowQuery(executionTime, 1000);
    }

    /**
     * 格式化执行时间
     * 
     * @param executionTime 执行时间(毫秒)
     * @return 格式化后的时间字符串
     */
    public static String formatExecutionTime(long executionTime)
    {
        if (executionTime < 1000)
        {
            return executionTime + "ms";
        }
        else if (executionTime < 60000)
        {
            return String.format("%.2fs", executionTime / 1000.0);
        }
        else
        {
            long minutes = executionTime / 60000;
            long seconds = (executionTime % 60000) / 1000;
            return String.format("%dm%ds", minutes, seconds);
        }
    }

    /**
     * 获取性能等级描述
     * 
     * @param executionTime 执行时间(毫秒)
     * @return 性能等级描述
     */
    public static String getPerformanceLevelDescription(long executionTime)
    {
        if (executionTime < 100)
        {
            return "优秀 - 查询响应非常快";
        }
        else if (executionTime < 500)
        {
            return "良好 - 查询响应较快";
        }
        else if (executionTime < 1000)
        {
            return "一般 - 查询响应正常";
        }
        else if (executionTime < 5000)
        {
            return "较差 - 查询响应较慢";
        }
        else
        {
            return "严重 - 查询响应很慢";
        }
    }

    /**
     * 生成性能监控摘要
     * 
     * @param metrics 性能监控指标
     * @return 摘要信息
     */
    public static String generatePerformanceSummary(DepartmentDataPerformanceMetrics metrics)
    {
        if (metrics == null)
        {
            return "无性能数据";
        }
        
        StringBuilder summary = new StringBuilder();
        summary.append("表: ").append(metrics.getTableName());
        summary.append(", 操作: ").append(metrics.getOperationType());
        
        if (metrics.getExecutionTime() != null)
        {
            long timeMs = metrics.getExecutionTime().longValue();
            summary.append(", 耗时: ").append(formatExecutionTime(timeMs));
        }
        
        if (metrics.getUsedDeptIndex() != null)
        {
            summary.append(", 使用索引: ").append(metrics.getUsedDeptIndex() ? "是" : "否");
        }
        
        if (metrics.getRowsScanned() != null)
        {
            summary.append(", 扫描行数: ").append(metrics.getRowsScanned());
        }
        
        if (metrics.getRowsReturned() != null)
        {
            summary.append(", 返回行数: ").append(metrics.getRowsReturned());
        }
        
        return summary.toString();
    }
}