package com.huangzj.protoplugin.mysql;

import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.PsiManager;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.ui.JBUI;
import com.huangzj.protoplugin.PackageInference;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class MysqlEntityDialog extends DialogWrapper {

    private final Project project;
    private final JTextField jdbcUrlField = new JTextField(40);
    private final JTextField userField = new JTextField(20);
    private final JPasswordField passwordField = new JPasswordField(20);
    private final JTextField filterField = new JTextField(20);
    private final TextFieldWithBrowseButton outputDirField = new TextFieldWithBrowseButton();
    private final JPanel tableListPanel = new JPanel();
    private final Map<String, javax.swing.JCheckBox> tableChecks = new LinkedHashMap<>();
    private List<String> loadedTables = Collections.emptyList();

    public MysqlEntityDialog(@Nullable Project project, @Nullable VirtualFile presetFolder) {
        super(project);
        this.project = project;
        setTitle("mysql转Jpa");
        if (presetFolder != null) {
            outputDirField.setText(presetFolder.getPath());
        }
        FileChooserDescriptor desc = FileChooserDescriptorFactory.createSingleFolderDescriptor();
        desc.setTitle("选择实体类输出目录（通常为 .../src/main/java/...）");
        outputDirField.addBrowseFolderListener("选择目录", null, project, desc);

        tableListPanel.setLayout(new BoxLayout(tableListPanel, BoxLayout.Y_AXIS));
        tableListPanel.setBorder(JBUI.Borders.empty(4));

        filterField.getDocument()
                .addDocumentListener(
                        new DocumentListener() {
                            @Override
                            public void insertUpdate(DocumentEvent e) {
                                applyFilter();
                            }

                            @Override
                            public void removeUpdate(DocumentEvent e) {
                                applyFilter();
                            }

                            @Override
                            public void changedUpdate(DocumentEvent e) {
                                applyFilter();
                            }
                        });

        init();
    }

    @Override
    protected JComponent createCenterPanel() {
        JPanel root = new JPanel(new BorderLayout(8, 8));

        JPanel form = new JPanel(new GridBagLayout());
        GridBagConstraints gc = new GridBagConstraints();
        gc.insets = new Insets(4, 4, 4, 4);
        gc.anchor = GridBagConstraints.WEST;
        gc.fill = GridBagConstraints.HORIZONTAL;
        gc.weightx = 0;
        gc.gridx = 0;
        gc.gridy = 0;
        form.add(new JLabel("JDBC URL:"), gc);
        gc.gridx = 1;
        gc.weightx = 1;
        jdbcUrlField.setToolTipText("例: jdbc:mysql://127.0.0.1:3306/dbname?useSSL=false&serverTimezone=UTC");
        form.add(jdbcUrlField, gc);

        gc.gridx = 0;
        gc.gridy = 1;
        gc.weightx = 0;
        form.add(new JLabel("用户名:"), gc);
        gc.gridx = 1;
        gc.weightx = 1;
        form.add(userField, gc);

        gc.gridx = 0;
        gc.gridy = 2;
        gc.weightx = 0;
        form.add(new JLabel("密码:"), gc);
        gc.gridx = 1;
        gc.weightx = 1;
        form.add(passwordField, gc);

        gc.gridx = 0;
        gc.gridy = 3;
        gc.weightx = 0;
        form.add(new JLabel("输出目录:"), gc);
        gc.gridx = 1;
        gc.weightx = 1;
        form.add(outputDirField, gc);

        JLabel hint =
                new JLabel(
                        "<html>提示：<b>必须包含库名</b>，例如 <code>host:3306/my_database</code> 或完整 JDBC URL。"
                                + "<br/>仅填 <code>host:3306</code> 无法列出表，且易导致连接异常。"
                                + "<br/>生成类使用 <code>jakarta.persistence</code> 注解。</html>");
        hint.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0));
        JPanel top = new JPanel(new BorderLayout(0, 8));
        top.add(hint, BorderLayout.NORTH);
        top.add(form, BorderLayout.CENTER);
        root.add(top, BorderLayout.NORTH);

        JPanel mid = new JPanel(new BorderLayout(4, 4));
        JPanel toolbar = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0));
        JButton queryBtn = new JButton("查询表");
        queryBtn.addActionListener(e -> runQueryTables());
        toolbar.add(queryBtn);
        toolbar.add(new JLabel("搜索表名:"));
        toolbar.add(filterField);
        mid.add(toolbar, BorderLayout.NORTH);

        JBScrollPane scroll = new JBScrollPane(tableListPanel);
        scroll.setPreferredSize(new Dimension(560, 260));
        mid.add(scroll, BorderLayout.CENTER);
        root.add(mid, BorderLayout.CENTER);

        JPanel south = new JPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0));
        JButton genBtn = new JButton("生成 Java 实体");
        genBtn.addActionListener(e -> runGenerateEntities());
        south.add(genBtn);
        root.add(south, BorderLayout.SOUTH);

        return root;
    }

    @Override
    protected Action @NotNull [] createActions() {
        return new Action[] {getCancelAction()};
    }

    @Override
    public void doCancelAction() {
        super.doCancelAction();
    }

    @Nullable
    @Override
    public JComponent getPreferredFocusedComponent() {
        return jdbcUrlField;
    }

    private void runQueryTables() {
        ValidationInfo v = validateConnectionFields();
        if (v != null) {
            Messages.showErrorDialog(project, v.message, "校验失败");
            return;
        }
        ProgressManager.getInstance()
                .run(
                        new Task.Backgroundable(project, "查询 MySQL 表", false) {
                            @Override
                            public void run(@NotNull ProgressIndicator indicator) {
                                indicator.setIndeterminate(true);
                                try (Connection conn =
                                        JdbcMySqlMetadata.openConnection(
                                                jdbcUrlField.getText().trim(),
                                                userField.getText().trim(),
                                                new String(passwordField.getPassword()))) {
                                    List<String> names = JdbcMySqlMetadata.listTableNames(conn);
                                    ApplicationManager.getApplication()
                                            .invokeLater(
                                                    () -> {
                                                        loadedTables = new ArrayList<>(names);
                                                        rebuildTableChecks();
                                                        Messages.showInfoMessage(
                                                                project,
                                                                "共读取 " + names.size() + " 张表。",
                                                                "查询完成");
                                                    });
                                } catch (Exception ex) {
                                    String msg = ex.getMessage() != null ? ex.getMessage() : ex.toString();
                                    ApplicationManager.getApplication()
                                            .invokeLater(
                                                    () ->
                                                            Messages.showErrorDialog(
                                                                    project, msg, "连接或查询失败"));
                                }
                            }
                        });
    }

    private void rebuildTableChecks() {
        tableChecks.clear();
        tableListPanel.removeAll();
        String q = filterField.getText().trim().toLowerCase();
        for (String t : loadedTables) {
            if (!q.isEmpty() && !t.toLowerCase().contains(q)) {
                continue;
            }
            javax.swing.JCheckBox cb = new javax.swing.JCheckBox(t, false);
            cb.putClientProperty("table", t);
            tableChecks.put(t, cb);
            tableListPanel.add(cb);
            tableListPanel.add(Box.createVerticalStrut(2));
        }
        tableListPanel.revalidate();
        tableListPanel.repaint();
    }

    private void applyFilter() {
        if (loadedTables.isEmpty()) {
            return;
        }
        Map<String, Boolean> selected = new LinkedHashMap<>();
        for (Map.Entry<String, javax.swing.JCheckBox> e : tableChecks.entrySet()) {
            selected.put(e.getKey(), e.getValue().isSelected());
        }
        rebuildTableChecks();
        for (Map.Entry<String, Boolean> e : selected.entrySet()) {
            javax.swing.JCheckBox cb = tableChecks.get(e.getKey());
            if (cb != null) {
                cb.setSelected(Boolean.TRUE.equals(e.getValue()));
            }
        }
    }

    private ValidationInfo validateConnectionFields() {
        String raw = jdbcUrlField.getText().trim();
        if (raw.isEmpty()) {
            return new ValidationInfo("请填写 JDBC URL 或 host:port/库名", jdbcUrlField);
        }
        if (!JdbcMySqlMetadata.hasDatabaseInUrl(raw)) {
            return new ValidationInfo(
                    "请在地址中带上数据库名，例如 43.155.238.153:3306/your_db 或完整 jdbc:mysql://host:3306/your_db",
                    jdbcUrlField);
        }
        return null;
    }

    private ValidationInfo validateOutputAndSelection() {
        String path = outputDirField.getText().trim();
        if (path.isEmpty()) {
            return new ValidationInfo("请选择输出目录", outputDirField);
        }
        VirtualFile dir = LocalFileSystem.getInstance().findFileByPath(path);
        if (dir == null || !dir.isDirectory()) {
            return new ValidationInfo("输出路径不是有效目录", outputDirField);
        }
        String pkg = PackageInference.inferPackage(dir);
        if (pkg.isEmpty()) {
            return new ValidationInfo(
                    "无法推断 Java 包名：请选择位于 .../src/main/java/<包路径>/ 下的目录。", outputDirField);
        }
        List<String> sel = getSelectedTables();
        if (sel.isEmpty()) {
            return new ValidationInfo("请至少选择一张表（可先点击「查询表」）", outputDirField);
        }
        return null;
    }

    private List<String> getSelectedTables() {
        List<String> list = new ArrayList<>();
        for (javax.swing.JCheckBox cb : tableChecks.values()) {
            if (cb.isSelected()) {
                Object t = cb.getClientProperty("table");
                if (t instanceof String) {
                    list.add((String) t);
                }
            }
        }
        return list;
    }

    private void runGenerateEntities() {
        ValidationInfo v1 = validateConnectionFields();
        if (v1 != null) {
            Messages.showErrorDialog(project, v1.message, "校验失败");
            return;
        }
        ValidationInfo v2 = validateOutputAndSelection();
        if (v2 != null) {
            Messages.showErrorDialog(project, v2.message, "校验失败");
            return;
        }
        VirtualFile outDir = LocalFileSystem.getInstance().findFileByPath(outputDirField.getText().trim());
        String pkg = PackageInference.inferPackage(outDir);
        List<String> tables = getSelectedTables();

        ProgressManager.getInstance()
                .run(
                        new Task.Backgroundable(project, "生成 JPA 实体", false) {
                            @Override
                            public void run(@NotNull ProgressIndicator indicator) {
                                indicator.setIndeterminate(false);
                                Map<String, String> sources = new LinkedHashMap<>();
                                try (Connection conn =
                                        JdbcMySqlMetadata.openConnection(
                                                jdbcUrlField.getText().trim(),
                                                userField.getText().trim(),
                                                new String(passwordField.getPassword()))) {
                                    int i = 0;
                                    for (String table : tables) {
                                        indicator.setFraction((double) i / Math.max(1, tables.size()));
                                        indicator.setText2(table);
                                        List<ColumnMeta> cols = JdbcMySqlMetadata.listColumns(conn, table);
                                        Set<String> pk = JdbcMySqlMetadata.primaryKeyColumns(conn, table);
                                        String src = JpaEntitySourceGenerator.generate(pkg, table, cols, pk);
                                        String className = JavaNames.toClassNameFromTable(table) + ".java";
                                        sources.put(className, src);
                                        i++;
                                    }
                                    indicator.setFraction(1.0);
                                } catch (Exception ex) {
                                    String msg = ex.getMessage() != null ? ex.getMessage() : ex.toString();
                                    ApplicationManager.getApplication()
                                            .invokeLater(
                                                    () ->
                                                            Messages.showErrorDialog(
                                                                    project, msg, "读取表结构失败"));
                                    return;
                                }
                                ApplicationManager.getApplication()
                                        .invokeLater(
                                                () ->
                                                        writeFilesOnEdt(
                                                                outDir, pkg, sources));
                            }
                        });
    }

    private void writeFilesOnEdt(
            VirtualFile outDir, String pkg, Map<String, String> sources) {
        if (project == null || outDir == null) {
            return;
        }
        int[] created = {0};
        int[] skipped = {0};
        WriteCommandAction.runWriteCommandAction(
                project,
                () -> {
                    PsiDirectory psiDir = PsiManager.getInstance(project).findDirectory(outDir);
                    if (psiDir == null) {
                        return;
                    }
                    PsiFileFactory factory = PsiFileFactory.getInstance(project);
                    for (Map.Entry<String, String> e : sources.entrySet()) {
                        String name = e.getKey();
                        if (psiDir.findFile(name) != null) {
                            skipped[0]++;
                            continue;
                        }
                        PsiFile psiFile =
                                factory.createFileFromText(name, JavaLanguage.INSTANCE, e.getValue());
                        psiDir.add(psiFile);
                        created[0]++;
                    }
                });
        Messages.showInfoMessage(
                project,
                "新建 " + created[0] + " 个实体文件，跳过已存在 " + skipped[0] + " 个。\n包名: " + pkg,
                "生成完成");
    }
}
