package easydo.technology.util;

import com.alibaba.fastjson.JSONObject;
import easydo.technology.utils.StringUtil;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class OracleResultSetUtil {
    private static Pattern linePattern = Pattern.compile("_(\\w)");
    private static String suffixBegin = "_BEGIN";
    private static String suffixEnd = "_END";
    private static String suffixLike = "_LIKE";
    private static String suffixNot = "_NOT";
    private static String suffixIn = "_IN";
    private static String suffixNotIn = "_NOTIN";
    private static String suffixNull = "_NULL";
    private static String suffixNotNull = "_NOTNULL";
    private static String suffixApplySql = "APPLY_SQL";
    private static Pattern humpPattern = Pattern.compile("[A-Z]");

    /**
     * 驼峰转下划线,再转大写
     */
    public static String humpToLine(String str) {
        Matcher matcher = humpPattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        if (sb.toString().startsWith("_")) {
            sb.deleteCharAt(0);
        }
        return sb.toString().toUpperCase();
    }

    public static String lineToHump(String str) {
        str = str.toLowerCase();
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    public static String formatSqlCondition(Map<String, Object> map, boolean isPage) throws RuntimeException {

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(" where 1=1 ");
        if (isPage) {
            stringBuilder.append("{replace_page_sql} ");
        }
        map.entrySet().stream()
                .filter(entry -> StringUtil.isNotEmpty(entry.getKey()) && StringUtil.isNotEmpty(entry.getValue()))
                .forEach(entry -> {
                    String key = entry.getKey();
                    Object value = entry.getValue();
                    if (containsSqlInjection(value) && !"apply_sql".equals(key)) {
                        throw new RuntimeException("非法参数");
                    }
                    String column = humpToLine(key);
                    if (column.endsWith(suffixBegin)) {
                        stringBuilder.append(" and ");
                        column = column.replace(suffixBegin, "");
                        stringBuilder.append(column);
                        stringBuilder.append(" >=").append("'").append(value).append("'");
                    } else if (column.endsWith(suffixEnd)) {
                        stringBuilder.append(" and ");
                        column = column.replace(suffixEnd, "");
                        stringBuilder.append(column);
                        stringBuilder.append(" <=").append("'").append(value).append("'");
                    } else if (column.endsWith(suffixLike)) {
                        stringBuilder.append(" and ");
                        column = column.replace(suffixLike, "");
                        stringBuilder.append(column);
                        stringBuilder.append(" like '%").append(value).append("%'");
                    } else if (column.endsWith(suffixNot)) {
                        stringBuilder.append(" and ");
                        column = column.replace(suffixNot, "");
                        stringBuilder.append(column).append(" !=");
                        if ("true".equals(value.toString()) || "false".equals(value.toString())) {
                            stringBuilder.append(value);
                        } else {
                            stringBuilder.append("'").append(value).append("'");
                        }
                    } else if (column.endsWith(suffixIn)) {
                        stringBuilder.append(" and ");
                        column = column.replace(suffixIn, "");
                        List<Object> list = (List<Object>) value;
                        stringBuilder.append(column);
                        stringBuilder.append(" in (");
                        if (!list.isEmpty()) {
                            list.forEach(str -> {
                                stringBuilder.append("'").append(str).append("',");
                            });
                        } else {
                            stringBuilder.append("'null',");
                        }
                        stringBuilder.deleteCharAt(stringBuilder.toString().length() - 1);
                        stringBuilder.append(")");
                    } else if (column.endsWith(suffixNotIn)) {
                        stringBuilder.append(" and ");
                        column = column.replace(suffixNotIn, "");
                        List<String> list = (List<String>) value;
                        stringBuilder.append(column);
                        stringBuilder.append(" not in (");
                        if (!list.isEmpty()) {
                            list.forEach(str -> {
                                stringBuilder.append("'").append(str).append("',");
                            });
                        } else {
                            stringBuilder.append("'null',");
                        }
                        stringBuilder.deleteCharAt(stringBuilder.toString().length() - 1);
                        stringBuilder.append(")");
                    } else if (column.endsWith(suffixNull)) {
                        stringBuilder.append(" and ");
                        column = column.replace(suffixNull, "");
                        stringBuilder.append(column).append(" is null");
                    } else if (column.endsWith(suffixNotNull)) {
                        stringBuilder.append(" and ");
                        column = column.replace(suffixNotNull, "");
                        stringBuilder.append(column).append(" is not null");
                    } else if (column.equals(suffixApplySql)) {
                        stringBuilder.append(" and ");
                        stringBuilder.append("(").append(value).append(")");
                    } else if (column.startsWith("(") && column.endsWith(")")) {
                        stringBuilder.append(" and ");
                        column = key.substring(0, key.length() - 1);
                        stringBuilder.append(column).append(" =").append("'").append(value).append("')");
                    } else {
                        stringBuilder.append(" and ");
                        stringBuilder.append(column).append(" =");
                        if ("true".equals(value.toString()) || "false".equals(value.toString())) {
                            stringBuilder.append(value);
                        } else {
                            stringBuilder.append("'").append(value).append("'");
                        }
                    }
                });
        return stringBuilder.toString();
    }

    public static <T> String formatPageSqlCondition(Map<String, Object> map, String tableName) {
        int current = (int) map.get("current");
        int size = (int) map.get("size");
        String orderBy = null;
        if (map.containsKey("orderBy")) {
            orderBy = map.get("orderBy").toString();
        }
        map.remove("current");
        map.remove("size");
        map.remove("orderBy");

        String condition = formatSqlCondition(map, true);
        if (StringUtil.isNotEmpty(orderBy)) {
            String[] orderByArr = orderBy.split("_");
            String orderBySql = " order by `" + humpToLine(orderByArr[0]) + "` " + orderByArr[1];
            condition += orderBySql;
        }
        condition = condition.replace("{replace_page_sql}", "and ROWNUM <=" + current * size);

        String sql = "select * from (select t.*,ROWNUM r from " + tableName + " t" + condition + ") WHERE r >" + (current - 1) * size;

        return sql;
    }

    public static <T> List<T> formatResultSetToList(ResultSet resultSet, Class<T> clazz) throws SQLException {
        List<T> resultList = new ArrayList<>();
        while (resultSet.next()) {
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            JSONObject jsonObject = new JSONObject();
            for (int i = 1; i <= columnCount; i++) {
                String key = lineToHump(metaData.getColumnName(i));
                if (key.endsWith("Time") || key.endsWith("Date")) {
                    jsonObject.put(key, formatDateAndTime(key, resultSet.getObject(i)));
                } else {
                    jsonObject.put(key, resultSet.getObject(i));
                }

            }
            T t = jsonObject.toJavaObject(clazz);
            resultList.add(t);
        }
        return resultList;
    }

    private static Object formatDateAndTime(String key, Object object) {
        if (key.endsWith("Time")) {
            try {
                String timeStr = object.toString();
                timeStr = timeStr.replace("T", "").substring(0, 19);
                return timeStr;
            } catch (Exception e) {
                return object;
            }
        } else if (key.endsWith("Date")) {
            try {
                String timeStr = object.toString();
                timeStr = timeStr.substring(0, 10);
                return timeStr;
            } catch (Exception e) {
                return object;
            }
        } else {
            return object;
        }
    }

    private static boolean containsSqlInjection(Object obj) {
        String objStr = obj.toString().toLowerCase();
        // 使用正则表达式匹配独立的SQL关键字，注意正则表达式需要根据实际需求调整
        String pattern = "\\b(and|exec|insert|select|drop|grant|alter|delete|update|count|chr|mid|master|truncate|char|declare|or|;)\\b";
        return objStr.matches(pattern);
    }
}
