package org.example.Utils;

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

public final class ExceptionSummaryUtil {
    private static final int MAX_CLIENT_MESSAGE_LENGTH = 300;
    private static final Pattern INVALID_TABLE_OR_VIEW =
            Pattern.compile("无效的表或视图名?\\s*[\\[【]([^\\]】]+)[\\]】]");
    private static final Pattern MEMBER_ACCESS_ERROR =
            Pattern.compile("(?s)(第\\s*\\d+\\s*行附近出现错误[:：]?\\s*)?\\R?\\s*(无法解析的成员访问表达式\\s*\\[[^\\]]+\\])");
    private static final Pattern LINE_ERROR =
            Pattern.compile("(?s)(第\\s*\\d+\\s*行附近出现错误[:：]?\\s*)\\R?\\s*([^\\r\\n\\t]+)");
    private static final Pattern SQL_BLOCK =
            Pattern.compile("(?is)\\bSQL\\s*\\[.*?\\];");
    private static final Pattern JAVA_CLASS_PREFIX =
            Pattern.compile("\\b(?:[a-zA-Z_$][\\w$]*\\.)+[A-Za-z_$][\\w$]*(?:Exception|Error)?\\s*:?\\s*");

    private ExceptionSummaryUtil() {
    }

    public static String summarizeForClient(Throwable throwable) {
        String rawMessage = collectThrowableMessages(throwable);
        if (isBlank(rawMessage)) {
            return "请联系管理员查看服务端日志";
        }

        String invalidTableOrView = extractInvalidTableOrView(rawMessage);
        if (!isBlank(invalidTableOrView)) {
            return invalidTableOrView;
        }

        String knownDatabaseError = extractKnownDatabaseError(rawMessage);
        if (!isBlank(knownDatabaseError)) {
            return abbreviate(knownDatabaseError, MAX_CLIENT_MESSAGE_LENGTH);
        }

        String normalizedMessage = removeSqlBlocks(rawMessage);
        String meaningfulMessage = extractMessageAfterMarker(normalizedMessage, "mag:");
        if (isBlank(meaningfulMessage)) {
            meaningfulMessage = normalizedMessage;
        }

        String cleaned = cleanTechnicalNoise(meaningfulMessage);
        if (isBlank(cleaned)) {
            cleaned = "请联系管理员查看服务端日志";
        }
        return abbreviate(cleaned, MAX_CLIENT_MESSAGE_LENGTH);
    }

    public static boolean containsMessage(Throwable throwable, String keyword) {
        if (isBlank(keyword)) {
            return false;
        }
        return collectThrowableMessages(throwable).contains(keyword);
    }

    private static String extractKnownDatabaseError(String message) {
        Matcher memberAccessMatcher = MEMBER_ACCESS_ERROR.matcher(message);
        if (memberAccessMatcher.find()) {
            return buildLineAwareMessage(memberAccessMatcher.group(1), memberAccessMatcher.group(2));
        }

        Matcher lineErrorMatcher = LINE_ERROR.matcher(message);
        if (lineErrorMatcher.find()) {
            String reason = cleanTechnicalNoise(lineErrorMatcher.group(2));
            if (!isBlank(reason) && !reason.contains("SQL [")) {
                return buildLineAwareMessage(lineErrorMatcher.group(1), reason);
            }
        }

        String stripped = cleanTechnicalNoise(removeSqlBlocks(message));
        if (stripped.contains("SQL语法错误")) {
            return abbreviate(stripped.substring(stripped.indexOf("SQL语法错误")), MAX_CLIENT_MESSAGE_LENGTH);
        }
        if (stripped.contains("权限")) {
            return abbreviate(stripped.substring(stripped.indexOf("权限")), MAX_CLIENT_MESSAGE_LENGTH);
        }
        if (stripped.contains("连接")) {
            return abbreviate(stripped.substring(stripped.indexOf("连接")), MAX_CLIENT_MESSAGE_LENGTH);
        }
        return "";
    }

    private static String buildLineAwareMessage(String linePrefix, String reason) {
        String cleanedReason = cleanTechnicalNoise(reason);
        if (isBlank(cleanedReason)) {
            return "";
        }
        String normalizedLinePrefix = normalizeLineErrorPrefix(linePrefix);
        return isBlank(normalizedLinePrefix) ? cleanedReason : normalizedLinePrefix + cleanedReason;
    }

    private static String normalizeLineErrorPrefix(String linePrefix) {
        if (isBlank(linePrefix)) {
            return "";
        }
        Matcher matcher = Pattern.compile("第\\s*(\\d+)\\s*行附近出现错误").matcher(linePrefix);
        if (matcher.find()) {
            return "第" + matcher.group(1) + "行附近出现错误：";
        }
        return "";
    }

    private static String collectThrowableMessages(Throwable throwable) {
        if (throwable == null) {
            return "";
        }

        StringBuilder message = new StringBuilder();
        Set<Throwable> seen = new HashSet<>();
        Throwable current = throwable;
        while (current != null && seen.add(current)) {
            if (!isBlank(current.getMessage())) {
                if (message.length() > 0) {
                    message.append(System.lineSeparator());
                }
                message.append(current.getMessage());
            }
            current = current.getCause();
        }
        return message.toString();
    }

    private static String removeSqlBlocks(String message) {
        return SQL_BLOCK.matcher(message).replaceAll("SQL [已省略]");
    }

    private static String extractInvalidTableOrView(String message) {
        Matcher matcher = INVALID_TABLE_OR_VIEW.matcher(message);
        if (matcher.find()) {
            return "无效的表或视图[" + matcher.group(1).trim() + "]";
        }
        return "";
    }

    private static String extractMessageAfterMarker(String message, String marker) {
        int index = message.lastIndexOf(marker);
        if (index < 0) {
            return "";
        }
        return message.substring(index + marker.length());
    }

    private static String cleanTechnicalNoise(String message) {
        StringBuilder cleaned = new StringBuilder();
        String normalized = message.replace("\r", "\n");
        for (String line : normalized.split("\n")) {
            String item = line.trim();
            if (isBlank(item) || isStackTraceNoise(item)) {
                continue;
            }
            item = stripCausePrefix(item);
            if (item.contains("Exception") || item.contains("Error")) {
                item = JAVA_CLASS_PREFIX.matcher(item).replaceAll("");
            }
            item = item.replace("execute sql error:", "");
            item = item.replace("StatementCallback;", "");
            item = item.replace("PreparedStatementCallback;", "");
            item = item.replace("bad SQL grammar [];", "");
            item = item.trim();
            if (isBlank(item) || isStackTraceNoise(item)) {
                continue;
            }
            if (cleaned.length() > 0) {
                cleaned.append("；");
            }
            cleaned.append(item);
        }
        return cleaned.toString().replaceAll("\\s+", " ").trim();
    }

    private static String stripCausePrefix(String message) {
        String result = message;
        while (result.startsWith("Caused by:")) {
            result = result.substring("Caused by:".length()).trim();
        }
        return result;
    }

    private static boolean isStackTraceNoise(String line) {
        return line.startsWith("at ")
                || line.startsWith("...")
                || line.contains("common frames omitted")
                || line.contains("StackTraceElement")
                || line.contains("JdbcTemplate.java:")
                || line.contains("org.springframework.jdbc.core.JdbcTemplate")
                || line.contains("SQLStateSQLExceptionTranslator")
                || line.contains("AbstractFallbackSQLExceptionTranslator");
    }

    private static String abbreviate(String message, int maxLength) {
        if (message.length() <= maxLength) {
            return message;
        }
        return message.substring(0, maxLength - 3) + "...";
    }

    private static boolean isBlank(String value) {
        return value == null || value.trim().isEmpty();
    }
}
