package com.huangzj.protoplugin.mysql;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/** 读取 MySQL 表名与列元数据。 */
public final class JdbcMySqlMetadata {

    private static volatile boolean driverLoaded;

    private JdbcMySqlMetadata() {}

    /**
     * IntelliJ 插件使用独立 ClassLoader 时，JDBC 4 的 SPI 往往不会自动注册 MySQL 驱动，
     * 会出现 “No suitable driver”。连接前显式加载驱动类可避免该问题。
     */
    public static void ensureMySqlDriverLoaded() throws SQLException {
        if (driverLoaded) {
            return;
        }
        synchronized (JdbcMySqlMetadata.class) {
            if (driverLoaded) {
                return;
            }
            try {
                Class.forName("com.mysql.cj.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                throw new SQLException(
                        "未找到 MySQL JDBC 驱动类 com.mysql.cj.jdbc.Driver。"
                                + "请使用 Gradle 执行 buildPlugin 重新打包插件，并确认已依赖 mysql-connector-j。",
                        e);
            }
            driverLoaded = true;
        }
    }

    /**
     * 简写地址须包含库名，例如 host:3306/dbname；否则元数据中的 catalog 为空，无法列出表。
     */
    public static boolean hasDatabaseInUrl(String raw) {
        String url = normalizeJdbcUrl(raw);
        if (!url.startsWith("jdbc:mysql://")) {
            return true;
        }
        String rest = url.substring("jdbc:mysql://".length());
        int q = rest.indexOf('?');
        String pathPart = q >= 0 ? rest.substring(0, q) : rest;
        int slash = pathPart.indexOf('/');
        return slash >= 0 && slash < pathPart.length() - 1;
    }

    public static String normalizeJdbcUrl(String raw) {
        String s = raw == null ? "" : raw.trim();
        if (s.isEmpty()) {
            return "";
        }
        if (s.startsWith("jdbc:mysql:")) {
            return s;
        }
        // host:port/db 或 host/db
        if (!s.contains("://")) {
            return "jdbc:mysql://" + s;
        }
        return s;
    }

    public static Connection openConnection(String jdbcUrl, String user, String password)
            throws SQLException {
        ensureMySqlDriverLoaded();
        String url = normalizeJdbcUrl(jdbcUrl);
        Properties props = new Properties();
        if (user != null && !user.isEmpty()) {
            props.setProperty("user", user);
        }
        if (password != null) {
            props.setProperty("password", password);
        }
        return DriverManager.getConnection(url, props);
    }

    public static List<String> listTableNames(Connection conn) throws SQLException {
        DatabaseMetaData md = conn.getMetaData();
        String catalog = conn.getCatalog();
        List<String> names = new ArrayList<>();
        try (ResultSet rs = md.getTables(catalog, null, "%", new String[] {"TABLE"})) {
            while (rs.next()) {
                String t = rs.getString("TABLE_NAME");
                if (t != null && !t.isEmpty()) {
                    names.add(t);
                }
            }
        }
        return names;
    }

    public static List<ColumnMeta> listColumns(Connection conn, String tableName)
            throws SQLException {
        DatabaseMetaData md = conn.getMetaData();
        String catalog = conn.getCatalog();
        List<ColumnMeta> cols = new ArrayList<>();
        try (ResultSet rs = md.getColumns(catalog, null, tableName, "%")) {
            while (rs.next()) {
                String col = rs.getString("COLUMN_NAME");
                int dataType = rs.getInt("DATA_TYPE");
                String typeName = rs.getString("TYPE_NAME");
                int size = rs.getInt("COLUMN_SIZE");
                int digits = rs.getInt("DECIMAL_DIGITS");
                boolean nullable = rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable;
                String ai = rs.getString("IS_AUTOINCREMENT");
                boolean autoInc = "YES".equalsIgnoreCase(ai);
                cols.add(new ColumnMeta(col, dataType, typeName, size, digits, nullable, autoInc));
            }
        }
        return cols;
    }

    /** 列名 -> 是否主键（用于单列/复合主键）。 */
    public static Set<String> primaryKeyColumns(Connection conn, String tableName)
            throws SQLException {
        DatabaseMetaData md = conn.getMetaData();
        String catalog = conn.getCatalog();
        Map<String, Integer> order = new LinkedHashMap<>();
        try (ResultSet rs = md.getPrimaryKeys(catalog, null, tableName)) {
            while (rs.next()) {
                String col = rs.getString("COLUMN_NAME");
                int seq = rs.getInt("KEY_SEQ");
                if (col != null) {
                    order.put(col, seq);
                }
            }
        }
        List<Map.Entry<String, Integer>> entries = new ArrayList<>(order.entrySet());
        entries.sort(Map.Entry.comparingByValue());
        Set<String> set = new LinkedHashSet<>();
        for (Map.Entry<String, Integer> e : entries) {
            set.add(e.getKey());
        }
        return set;
    }
}
