package org.example.Utils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 正则表达式工具类：SQL Server 转 达梦数据库 SQL 适配工具
 * 核心解决：STUFF+FOR XML PATH、ISNULL、WITH(NOLOCK)、dbo.前缀、单引号别名、分号解析、中括号等兼容问题
 */
public class RegexUtil {
    /**
     * 检测字符串是否包含中文字符
     */
    public static final Pattern HasCNReg = Pattern.compile("[\u4e00-\u9fa5]");

    /**
     * 检测字符串是否包含英文字母
     */
    public static final Pattern HasENReg = Pattern.compile("[a-zA-Z]");

    /**
     * 验证字符串是否仅由英文字母组成
     */
    public static final Pattern ENOnlyReg = Pattern.compile("^[a-zA-Z]+$");

    /**
     * 检测字符串是否包含数字
     */
    public static final Pattern HasNumReg = Pattern.compile("\\d");

    /**
     * 验证用户名格式（4-30位，支持中文、字母、数字、下划线和非空白字符）
     */
    public static final Pattern UserNameReg = Pattern.compile("^[\\w|\\S|(\\u4e00-\\u9fa5)]{4,30}$");

    /**
     * 验证密码格式（6-20位，支持字母、数字、下划线和非空白字符）
     */
    public static final Pattern PasswordReg = Pattern.compile("^[\\w|\\S]{6,20}$");

    /**
     * 验证支付密码格式（6位数字）
     */
    public static final Pattern PayPasswordReg = Pattern.compile("[\\d]{6,6}$");

    /**
     * 验证手机号码格式（11位数字）
     */
    public static final Pattern PhoneReg = Pattern.compile("^[0-9]{11,11}$");

    /**
     * 验证字符串是否仅由数字组成
     */
    public static final Pattern NumberReg = Pattern.compile("^[0-9]+$");

    /**
     * 验证手机验证码格式（4位数字）
     */
    public static final Pattern PhoneCodeReg = Pattern.compile("^[0-9]{4,4}$");

    /**
     * 验证通用验证码格式（4位字母数字组合）
     */
    public static final Pattern CheckCodeReg = Pattern.compile("^[0-9a-zA-Z]{4,4}$");

    /**
     * 验证邮箱地址格式
     */
    public static final Pattern EmailReg = Pattern.compile("^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$");

    /**
     * 验证时间格式（YYYY-MM-DD 或 YYYY/MM/DD 或 YYYY:MM:DD）
     */
    public static final Pattern TimeReg = Pattern.compile("^2([0-9]{3})([-|/|:]{1})([0-9]{1,2})([-|/|:]{1})([0-9]{1,2})$");


    public static Pattern getWeekPwdRegex() {
        String regStr = WebConfigUtil.get("WeekPwdReg", "(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9]).{8,18}");
        // RegexOptions.Multiline → Pattern.MULTILINE
        // RegexOptions.IgnorePatternWhitespace → Pattern.COMMENTS
        return Pattern.compile(regStr, Pattern.MULTILINE | Pattern.COMMENTS);
    }

    public static void setWeekPwdRegex(Pattern weekPwdRegex) {
        WeekPwdRegex = weekPwdRegex;
    }

    private static Pattern WeekPwdRegex;

    // -------------------------- 达梦SQL适配核心正则 --------------------------
    // 1. 移除dbo.前缀（忽略大小写）
    private static final Pattern DBO_PREFIX_PATTERN = Pattern.compile("\\bdbo\\.", Pattern.CASE_INSENSITIVE);
    // 匹配 model：显式兼容 a.model、model.aaa、as model、model as 等所有场景
// 正则说明：
// (?<=[^a-zA-Z0-9_\"]|\\.)  正向后顾：model前面是「非字母/数字/下划线/双引号」 或 「.点号」
// model                     匹配model关键字
// (?=[^a-zA-Z0-9_\"]|\\.)   正向前瞻：model后面是「非字母/数字/下划线/双引号」 或 「.点号」
// Pattern.CASE_INSENSITIVE  忽略大小写（兼容 MODEL、Model 等写法）
    private static final Pattern INDEPENDENT_MODEL_PATTERN = Pattern.compile(
            "(?<=[^a-zA-Z0-9_\"]|\\.)model(?=[^a-zA-Z0-9_\"]|\\.)",
            Pattern.CASE_INSENSITIVE
    );

    // 匹配 new：逻辑和 model 完全一致，仅替换关键字为 new
    private static final Pattern INDEPENDENT_NEW_PATTERN = Pattern.compile(
            "(?<=[^a-zA-Z0-9_\"]|\\.)new(?=[^a-zA-Z0-9_\"]|\\.)",
            Pattern.CASE_INSENSITIVE
    );

    //匹配list
    private static final Pattern INDEPENDENT_list_PATTERN = Pattern.compile(
            "(?<=[^a-zA-Z0-9_\"]|\\.)list(?=[^a-zA-Z0-9_\"]|\\.)",
            Pattern.CASE_INSENSITIVE
    );

    //匹配RowNum

    private static final Pattern INDEPENDENT_RowNum_PATTERN = Pattern.compile(
            "(?<=[^a-zA-Z0-9_\"]|\\.)RowNum(?=[^a-zA-Z0-9_\"]|\\.)",
            Pattern.CASE_INSENSITIVE
    );

    // 3. 匹配as后单引号包裹的列别名（如 as '到访位置'）
    private static final Pattern ALIAS_SINGLE_QUOTE_PATTERN = Pattern.compile("(?i)(as)\\s*'([^']+)'");
    // 4. 匹配SQLServer的with(rowlock)行锁语法
    private static final Pattern SQLSERVER_ROWLOCK_PATTERN = Pattern.compile("(?i)\\s+with\\s*\\(\\s*rowlock\\s*\\)");

    //    修改后的sql有问题,暂时弃用  2026.1.23
    // 5. 匹配STUFF+FOR XML PATH的完整拼接块（增强版，兼容TYPE/MAX/数字长度）
    // 核心正则：匹配 STUFF 开始到 AS 前的完整片段
    // 核心：STUFF.*?(?=\\s+AS)  → 非贪婪匹配STUFF后所有字符，直到遇到" AS"（正向预查，不包含AS）

    private static final Pattern STUFF_TO_AS_PATTERN = Pattern.compile(
            "STUFF.*?(?=\\s+AS)",
            Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE
    );

    // 6. 匹配WITH(NOLOCK)表提示
    private static final Pattern NOLOCK_PATTERN = Pattern.compile("\\s+WITH\\s*\\(\\s*NOLOCK\\s*\\)", Pattern.CASE_INSENSITIVE);
    // 7. 匹配ISNULL函数
    private static final Pattern ISNULL_PATTERN = Pattern.compile("ISNULL\\s*\\(\\s*(.+?)\\s*,\\s*(.+?)\\s*\\)", Pattern.CASE_INSENSITIVE);
    // 8. 匹配NVARCHAR(MAX)
    private static final Pattern NVARCHAR_MAX_PATTERN = Pattern.compile("NVARCHAR\\s*\\(\\s*MAX\\s*\\)", Pattern.CASE_INSENSITIVE);
    // 9. 匹配多余空白字符（空格/换行/制表符）
    private static final Pattern BLANK_SPACE_PATTERN = Pattern.compile("\\s+");
    // 10.匹配.后的至少含有一个中文字符
//    private static final Pattern DOT_CN_IDENTIFIER_PATTERN = Pattern.compile("(?<=\\.)[^,，\\s]*[\\u4e00-\\u9fa5][^,，\\s]*");


    // 匹配--开头的单行注释（兼容换行/空格）
    private static final Pattern SQL_LINE_COMMENT_PATTERN = Pattern.compile(
            "--.*?(?=\\r|\\n|$)",
            Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE
    );

    // 仅匹配--开头的注释内容
//    private static final Pattern EXACT_COMMENT_PATTERN = Pattern.compile("--[^\\s]*");
    private static final Pattern COMMENT_PATTERN = Pattern.compile("(?<!')--.*$", Pattern.MULTILINE);

    /**
     * 匹配SQL字符串内的单引号（用于转义嵌套单引号）
     * 正则说明：
     * '([^']*)'       匹配单引号包裹的字符串（捕获组1为字符串内容）
     * (?<!')          排除已转义的单引号（避免重复转义）
     */
    private static final Pattern ESCAPE_SINGLE_QUOTE_PATTERN = Pattern.compile("'([^']*)'", Pattern.DOTALL);

    /**
     * 移除SQL中所有--开头的单行注释
     *
     * @param originalSql 原始SQL
     * @return 移除注释后的干净SQL
     */
    public static String removeLineComment(String originalSql) {
        if (originalSql == null || originalSql.isEmpty()) {
            return originalSql;
        }
        // 第一步：移除--注释
        Matcher matcher = SQL_LINE_COMMENT_PATTERN.matcher(originalSql);
        String cleanSql = matcher.replaceAll("");

        // 第二步：清理注释移除后多余的空格/换行（可选，优化格式）
        cleanSql = cleanSql.replaceAll("\\s+", " ").trim();

        return cleanSql;
    }

    /**
     * 适配达梦数据库的SQL语句（核心方法）
     * 解决所有关键兼容问题：
     * 1. 移除dbo.前缀  2. model单词加双引号  3. as后单引号别名转双引号
     * 4. 移除with(rowlock)  5. 移除中括号[]  6. 处理括号内分号
     * 7. 替换STUFF+FOR XML PATH为LISTAGG/WM_CONCAT  8. 移除WITH(NOLOCK)
     * 9. ISNULL→NVL  10. NVARCHAR(MAX)→VARCHAR(4000)  11. 压缩多余空白
     *
     * @param originalSql 原始SQL Server SQL（可为null）
     * @param useListAgg  是否使用LISTAGG（达梦8.0+推荐，低版本用WM_CONCAT）
     * @return 适配后的达梦SQL，入参为null则返回null
     */
    public static String processDmServerSql(String originalSql, boolean useListAgg) {
        // 空值处理
        if (originalSql == null || originalSql.trim().isEmpty()) {
            return originalSql;
        }

        String processedSql = originalSql;

        //0. 去掉注释
        processedSql = COMMENT_PATTERN.matcher(processedSql).replaceAll("");

        // 新增：1. 自动转义字符串内的嵌套单引号（核心逻辑）
        processedSql = escapeNestedSingleQuote(processedSql);
        // ========== 第一步：基础语法清理（先处理不影响核心逻辑的兼容点） ==========

        // 5. 移除中括号[]（SQL Server标识符，达梦不支持）
        processedSql = processedSql.replace("[", "").replace("]", "");

        // 1. 移除dbo.前缀
        processedSql = DBO_PREFIX_PATTERN.matcher(processedSql).replaceAll("");
        //        新增 替换 . 后含中文字符的标识符为"包裹形式
//        processedSql = DOT_CN_IDENTIFIER_PATTERN.matcher(processedSql).replaceAll("\"$0\"");
        // 2. 独立model单词加双引号
        processedSql = INDEPENDENT_MODEL_PATTERN.matcher(processedSql).replaceAll("\"model\"");
        //独立new单词加双引号
        processedSql = INDEPENDENT_NEW_PATTERN.matcher(processedSql).replaceAll("\"new\"");
        //list处理
        processedSql = INDEPENDENT_list_PATTERN.matcher(processedSql).replaceAll("\"list\"");
        //RowNum处理
        processedSql = INDEPENDENT_RowNum_PATTERN.matcher(processedSql).replaceAll("\"RowNum\"");
        // 3. as后单引号别名转双引号（不影响字符串常量）
        processedSql = ALIAS_SINGLE_QUOTE_PATTERN.matcher(processedSql).replaceAll("$1 \"$2\"");
        // 4. 移除with(rowlock)行锁语法
        processedSql = SQLSERVER_ROWLOCK_PATTERN.matcher(processedSql).replaceAll("");

        // 6. 递归移除括号内的分号（解决子查询分号解析错误）
        processedSql = removeSemicolonInBrackets(processedSql);
//        processedSql = processedSql.replace(";","");

        // ========== 第二步：核心SQL Server语法替换 ==========

        //        替换后的sql有问题,暂时弃用  2026.1.23(多出需要替换,每个sql不能精准匹配上)
        // 7. 替换STUFF+FOR XML PATH为达梦拼接函数
//        processedSql = replaceStuffXmlPath(processedSql, useListAgg);
        // 8. 移除WITH(NOLOCK)表提示
        processedSql = NOLOCK_PATTERN.matcher(processedSql).replaceAll("");
        // 9. ISNULL→NVL（达梦标准函数）
        processedSql = replaceIsnullToNvl(processedSql);
        // 10. NVARCHAR(MAX)→VARCHAR(4000)（达梦无MAX关键字）
        processedSql = NVARCHAR_MAX_PATTERN.matcher(processedSql).replaceAll("VARCHAR(4000)");


        // ========== 第三步：格式优化（避免解析异常） ==========
        // 11. 压缩多余空白为单个空格
//        processedSql = BLANK_SPACE_PATTERN.matcher(processedSql).replaceAll(" ");

        //12.去掉注释,去除首尾空白
//        processedSql = removeLineComment(processedSql);
        // 12. 去除首尾空白
        processedSql = processedSql.trim();

        return processedSql;
    }

    /**
     * 重载方法：默认使用LISTAGG（达梦8.0+）
     */
    public static String processDmServerSql(String originalSql) {
        return processDmServerSql(originalSql, true);
    }

    /**
     * 自动转义SQL字符串内的嵌套单引号（将'转为''）
     * 仅处理单引号包裹的字符串内的单引号，不影响语法级单引号
     */
    private static String escapeNestedSingleQuote(String sql) {
        if (sql == null || sql.isEmpty()) {
            return sql;
        }
        Matcher matcher = ESCAPE_SINGLE_QUOTE_PATTERN.matcher(sql);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            // 捕获组1：单引号内的字符串内容
            String content = matcher.group(1);
            // 转义内容中的单引号（' → ''）
            String escapedContent = content.replace("'", "''");
            // 替换回原字符串（保留外层单引号）
            matcher.appendReplacement(sb, "'" + Matcher.quoteReplacement(escapedContent) + "'");
        }
        matcher.appendTail(sb);
        return sb.toString();
    }


    /**
     * 移除括号内 SQL 语句末尾的分号（支持嵌套括号）
     * 解决达梦数据库因子查询末尾分号导致的语法错误
     *
     * @param sql 原始 SQL 语句
     * @return 修复后的 SQL 语句
     */
    public static String removeSemicolonInBrackets(String sql) {
        // 空值直接返回
        if (sql == null || sql.isEmpty()) {
            return sql;
        }

        // 正则匹配括号及内部内容（非贪婪匹配，支持嵌套）
        Pattern bracketPattern = Pattern.compile("\\((.*?)\\)", Pattern.DOTALL);
        Matcher matcher = bracketPattern.matcher(sql);
        StringBuffer sb = new StringBuffer();

        while (matcher.find()) {
            String innerContent = matcher.group(1);
            // 先递归处理嵌套的括号内容
            String processedInner = removeSemicolonInBrackets(innerContent);
            // 移除当前括号内语句末尾的分号（处理多个分号/分号前后空格的情况）
            processedInner = processedInner.replaceAll("\\s*;\\s*$", "");
            // 将处理后的内容放回括号中
            matcher.appendReplacement(sb, "(" + processedInner + ")");
        }
        // 拼接剩余未匹配的内容
        matcher.appendTail(sb);

        return sb.toString();
    }

    /**
     * 替换STUFF片段为达梦SQL
     *
     * @param originalSql 原始SQL
     * @param useListAgg  true=达梦8.0+（LISTAGG），false=低版本（WM_CONCAT）
     * @return 替换后的SQL
     */
    public static String replaceStuffXmlPath(String originalSql, boolean useListAgg) {
        if (originalSql == null || originalSql.isEmpty()) {
            return originalSql;
        }

        Matcher matcher = STUFF_TO_AS_PATTERN.matcher(originalSql);
        StringBuffer sb = new StringBuffer();

        while (matcher.find()) {
            // 1. 获取STUFF到AS前的完整片段（如：STUFF(( SELECT ',' + spec ... ), 1, 1, '')）
            String stuffFragment = matcher.group().trim();

            // 2. 手动解析片段，提取关键信息
            String concatField = extractConcatField(stuffFragment);       // 拼接字段（spec）
            String fromClause = extractFromClause(stuffFragment);         // FROM子句（含JOIN）
            String whereClause = extractWhereClause(stuffFragment);       // WHERE条件

            // 3. 组装达梦SQL
            String dmSql = buildDmConcatSql(concatField, fromClause, whereClause, useListAgg);

            // 4. 替换匹配到的STUFF片段
            matcher.appendReplacement(sb, Matcher.quoteReplacement(dmSql));
        }
        matcher.appendTail(sb);

        return sb.toString();
    }

    // -------------------------- 手动解析核心方法 --------------------------

    /**
     * 提取拼接字段（如：spec）
     * 逻辑：找到 SELECT ',' + 后的字段名
     */
    private static String extractConcatField(String stuffFragment) {
        // 匹配 SELECT ',' + 后的字段（兼容空格/换行）
        Pattern fieldPattern = Pattern.compile("SELECT\\s+','\\s*\\+\\s*(\\w+)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher matcher = fieldPattern.matcher(stuffFragment);
        return matcher.find() ? matcher.group(1).trim() : "''"; // 兜底返回空字符串
    }

    /**
     * 提取FROM子句（含JOIN，如：Crm_BillPolisttab left join p_ProductTab on productid = crm_lpo_productid）
     * 逻辑：找到 FROM 后 到 WHERE 前的内容
     */
    private static String extractFromClause(String stuffFragment) {
        Pattern fromPattern = Pattern.compile("FROM\\s+(.*?)\\s+WHERE", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher matcher = fromPattern.matcher(stuffFragment);
        return matcher.find() ? matcher.group(1).trim() : "";
    }

    /**
     * 提取WHERE子句（如：crm_lpo_billdocument_id = a.crm_mpo_billdocument_id）
     * 逻辑：找到 WHERE 后 到 FOR XML 前的内容
     */
    private static String extractWhereClause(String stuffFragment) {
        Pattern wherePattern = Pattern.compile("WHERE\\s+(.*?)\\s+FOR\\s+XML", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher matcher = wherePattern.matcher(stuffFragment);
        return matcher.find() ? matcher.group(1).trim() : "";
    }

    /**
     * 组装达梦拼接SQL
     */
    private static String buildDmConcatSql(String field, String fromClause, String whereClause, boolean useListAgg) {
        if (field.isEmpty() || fromClause.isEmpty()) {
            return "''"; // 兜底返回空字符串
        }
        // 拼接WHERE条件（如果有）
        String whereSql = whereClause.isEmpty() ? "" : " WHERE " + whereClause;

        if (useListAgg) {
            // 达梦8.0+：LISTAGG
            return String.format("(SELECT LISTAGG(%s, ',') WITHIN GROUP (ORDER BY %s) FROM %s%s) ",
                    field, field, fromClause, whereSql);
        } else {
            // 达梦低版本：WM_CONCAT + SUBSTR 去开头逗号
            return String.format("(SELECT SUBSTR(WM_CONCAT(%s), 2) FROM %s%s) ",
                    field, fromClause, whereSql);
        }
    }

    /**
     * 替换ISNULL(字段, 默认值)为NVL(字段, 默认值)
     */
    private static String replaceIsnullToNvl(String sql) {
        Matcher matcher = ISNULL_PATTERN.matcher(sql);
        StringBuffer sb = new StringBuffer();

        while (matcher.find()) {
            String field = matcher.group(1).trim();
            String defaultValue = matcher.group(2).trim();
            // 处理默认值为字符串的情况（保留单引号）
            String nvlExpr = String.format("NVL(%s, %s)", field, defaultValue);
            matcher.appendReplacement(sb, Matcher.quoteReplacement(nvlExpr));
        }
        matcher.appendTail(sb);

        return sb.toString();
    }

    /**
     * 转义SQL字符串中的单引号
     * 规则：将单个单引号 ' 替换为两个单引号 ''
     * @param originalStr 原始字符串（可能包含单引号）
     * @return 转义后的字符串（可直接嵌入SQL语句）
     */
    public static String escapeSingleQuote(String originalStr) {
        // 空值处理，避免空指针
        if (originalStr == null) {
            return null;
        }
        // 核心逻辑：替换单引号
        return originalStr.replace("'", "''");
    }

    /**
     * 快速构建带转义的SQL条件（针对字符串等值匹配）
     * @param columnName 数据库列名
     * @param columnValue 列值（原始值，自动转义）
     * @return 拼接好的SQL条件片段，例如：mes_ws_no='!P_create_billdocumentPr(''4000101'','''')'
     */
    public static String buildEqCondition(String columnName, String columnValue) {
        if (columnName == null || columnName.trim().isEmpty()) {
            throw new IllegalArgumentException("列名不能为空");
        }
        String escapedValue = escapeSingleQuote(columnValue);
        return String.format("%s='%s'", columnName.trim(), escapedValue);
    }
}