package com.bsphp.car;

import com.google.gson.JsonObject;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.net.URL;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

final class CarMainFrame extends JFrame {
    private final AccountBSPHPClient client = new AccountBSPHPClient();
    private final JTextArea notice = new JTextArea(3, 40);
    private final JLabel status = new JLabel("待操作");
    private final String machineCode = "JAVA-" + Integer.toHexString(System.identityHashCode(this));
    private final List<JLabel> captchaLabels = new ArrayList<>();
    private static final Font UI_FONT = new Font(Font.SANS_SERIF, Font.PLAIN, 12);
    private static final int INPUT_HEIGHT = 32;
    private static final int ROW_GAP = 20;
    private static final String TYPE_LOGIN = "INGES_LOGIN";
    private static final String TYPE_BACKTO = "INGES_MACK";
    private static final String TYPE_FEEDBACK = "INGES_SAY";
    private ConsoleDialog consoleDialog;
    private JPanel loginCaptchaRow;
    private JPanel backtoCaptchaRow;
    private JPanel feedbackCaptchaRow;
    private boolean loginCodeEnabled = true;
    private boolean backtoCodeEnabled = true;
    private boolean feedbackCodeEnabled = true;
    private static final DateTimeFormatter TIME_FMT = DateTimeFormatter.ofPattern("HH:mm:ss");

    CarMainFrame() {
        super("BSPHP 账号演示 (bsphp.java.car)");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(960, 680);
        setLocationRelativeTo(null);

        notice.setEditable(false);
        JPanel noticeBox = new JPanel(new BorderLayout());
        noticeBox.setBorder(BorderFactory.createTitledBorder("公告"));
        noticeBox.add(new JScrollPane(notice), BorderLayout.CENTER);

        JTabbedPane tabs = new JTabbedPane();
        tabs.addTab("密码登录", loginTab());
        tabs.addTab("短信登录", smsLoginTab());
        tabs.addTab("邮箱登录", emailLoginTab());
        tabs.addTab("账号注册", registerTab());
        tabs.addTab("短信注册", smsRegisterTab());
        tabs.addTab("邮箱注册", emailRegisterTab());
        tabs.addTab("解绑", unbindTab());
        tabs.addTab("充值", rechargeTab());
        tabs.addTab("短信找回", smsResetTab());
        tabs.addTab("邮箱找回", emailResetTab());
        tabs.addTab("找回密码", backtoTab());
        tabs.addTab("修改密码", changePwdTab());
        tabs.addTab("意见反馈", feedbackTab());

        JPanel root = new JPanel(new BorderLayout(6, 6));
        root.setBorder(BorderFactory.createEmptyBorder(8, 10, 8, 10));
        root.add(noticeBox, BorderLayout.NORTH);
        root.add(tabs, BorderLayout.CENTER);
        root.add(status, BorderLayout.SOUTH);
        setContentPane(root);

        initBootstrap();
    }

    private JPanel loginTab() {
        JTextField user = new JTextField("admin", 18);
        JTextField pwd = new JTextField("admin", 14);
        JTextField code = new JTextField(8);
        JPanel p = formPanel();
        p.add(wrapRow("账号", user));
        p.add(wrapRow("密码", pwd));
        loginCaptchaRow = wrapCaptchaRow("验证码", code);
        p.add(loginCaptchaRow);
        JButton login = new JButton("登录");
        login.addActionListener(e -> new SwingWorker<Void, Void>() {
            @Override protected Void doInBackground() throws Exception {
                JsonObject r = client.login(user.getText().trim(), pwd.getText(), code.getText().trim());
                String m = msg(r);
                String codeText = codeText(r);
                javax.swing.SwingUtilities.invokeLater(() -> {
                    status.setText(logLine("[login.lg]", codeText, m));
                    Integer codeNum = code(r);
                    if (codeNum != null && (codeNum == 1011 || codeNum == 9908)) openConsole();
                });
                return null;
            }
        }.execute());
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 2));
        row.add(login);
        row.add(callBtn("网络测试", "internet.in"));
        row.add(callBtn("版本", "v.in"));
        p.add(row);
        return p;
    }

    private JPanel smsLoginTab() {
        JTextField mobile = new JTextField(14);
        JTextField area = new JTextField("86", 6);
        JTextField coode = new JTextField(8);
        JTextField smsCode = new JTextField(8);
        JTextField key = new JTextField(machineCode, 22);
        JTextField maxoror = new JTextField(machineCode, 22);
        JPanel p = formPanel();
        p.add(wrapRow("手机号", mobile));
        p.add(wrapRow("区号", area));
        p.add(wrapCaptchaRow("图形验证码", coode));
        p.add(wrapRow("短信验证码", smsCode));
        p.add(wrapRow("key", key));
        p.add(wrapRow("maxoror", maxoror));
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 2));
        row.add(btn("发送验证码", () -> callAsync("[send_sms.lg]", () ->
                client.sendSmsCode("login", mobile.getText().trim(), d(area, "86"), coode.getText().trim()))));
        row.add(btn("短信登录", () -> callAsync("[login_sms.lg]",
                () -> client.loginSms(mobile.getText().trim(), d(area, "86"), smsCode.getText().trim(),
                        key.getText().trim(), maxoror.getText().trim(), coode.getText().trim()),
                true)));
        p.add(row);
        return p;
    }

    private JPanel emailLoginTab() {
        JTextField email = new JTextField(20);
        JTextField coode = new JTextField(8);
        JTextField emailCode = new JTextField(10);
        JTextField key = new JTextField(machineCode, 22);
        JTextField maxoror = new JTextField(machineCode, 22);
        JPanel p = formPanel();
        p.add(wrapRow("邮箱", email));
        p.add(wrapCaptchaRow("图形验证码", coode));
        p.add(wrapRow("邮箱验证码", emailCode));
        p.add(wrapRow("key", key));
        p.add(wrapRow("maxoror", maxoror));
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 2));
        row.add(btn("发送验证码", () -> callAsync("[send_email.lg]", () ->
                client.sendEmailCode("login", email.getText().trim(), coode.getText().trim()))));
        row.add(btn("邮箱登录", () -> callAsync("[login_email.lg]",
                () -> client.loginEmail(email.getText().trim(), emailCode.getText().trim(),
                        key.getText().trim(), maxoror.getText().trim(), coode.getText().trim()),
                true)));
        p.add(row);
        return p;
    }

    private JPanel registerTab() {
        JTextField user = new JTextField(18);
        JTextField pwd = new JTextField(14);
        JTextField pwdb = new JTextField(14);
        JTextField qq = new JTextField(12);
        JTextField mail = new JTextField(16);
        JTextField mobile = new JTextField(14);
        JComboBox<String> q = new JComboBox<>(new String[]{
                "你最喜欢的颜色？", "你母亲的名字？", "你父亲的名字？", "你的出生地？", "你最喜欢的食物？", "你的小学名称？", "自定义问题"
        });
        JTextField daan = new JTextField(18);
        JTextField code = new JTextField(8);
        JTextField ext = new JTextField(10);
        JPanel p = formPanel();
        p.add(wrapRow("账号", user));
        p.add(wrapRow("密码", pwd));
        p.add(wrapRow("确认密码", pwdb));
        p.add(wrapRow("QQ", qq));
        p.add(wrapRow("邮箱", mail));
        p.add(wrapRow("手机", mobile));
        p.add(wrapRow("密保问题", q));
        p.add(wrapRow("密保答案", daan));
        p.add(wrapCaptchaRow("验证码", code));
        p.add(wrapRow("推广码", ext));
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 2));
        row.add(btn("注册", () -> callAsync("[registration.lg]", () ->
                client.registerAccount(user.getText().trim(), pwd.getText(), pwdb.getText(), code.getText().trim(),
                        mobile.getText().trim(), String.valueOf(q.getSelectedItem()), daan.getText().trim(),
                        qq.getText().trim(), mail.getText().trim(), ext.getText().trim()))));
        p.add(row);
        return p;
    }

    private JPanel smsRegisterTab() {
        JTextField mobile = new JTextField(14);
        JTextField area = new JTextField("86", 6);
        JTextField coode = new JTextField(8);
        JTextField smsCode = new JTextField(8);
        JTextField user = new JTextField(14);
        JTextField pwd = new JTextField(12);
        JTextField pwdb = new JTextField(12);
        JTextField key = new JTextField(machineCode, 22);
        JPanel p = formPanel();
        p.add(wrapRow("手机号", mobile));
        p.add(wrapRow("区号", area));
        p.add(wrapCaptchaRow("图形验证码", coode));
        p.add(wrapRow("短信验证码", smsCode));
        p.add(wrapRow("账号", user));
        p.add(wrapRow("密码", pwd));
        p.add(wrapRow("确认密码", pwdb));
        p.add(wrapRow("key", key));
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 2));
        row.add(btn("发送验证码", () -> callAsync("[send_sms.lg]", () ->
                client.sendSmsCode("register", mobile.getText().trim(), d(area, "86"), coode.getText().trim()))));
        row.add(btn("短信注册", () -> callAsync("[register_sms.lg]", () ->
                client.registerSms(user.getText().trim(), mobile.getText().trim(), d(area, "86"),
                        smsCode.getText().trim(), pwd.getText(), pwdb.getText(), key.getText().trim(), coode.getText().trim()))));
        p.add(row);
        return p;
    }

    private JPanel emailRegisterTab() {
        JTextField email = new JTextField(20);
        JTextField coode = new JTextField(8);
        JTextField emailCode = new JTextField(10);
        JTextField user = new JTextField(14);
        JTextField pwd = new JTextField(12);
        JTextField pwdb = new JTextField(12);
        JTextField key = new JTextField(machineCode, 22);
        JPanel p = formPanel();
        p.add(wrapRow("邮箱", email));
        p.add(wrapCaptchaRow("图形验证码", coode));
        p.add(wrapRow("邮箱验证码", emailCode));
        p.add(wrapRow("账号", user));
        p.add(wrapRow("密码", pwd));
        p.add(wrapRow("确认密码", pwdb));
        p.add(wrapRow("key", key));
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 2));
        row.add(btn("发送验证码", () -> callAsync("[send_email.lg]", () ->
                client.sendEmailCode("register", email.getText().trim(), coode.getText().trim()))));
        row.add(btn("邮箱注册", () -> callAsync("[register_email.lg]", () ->
                client.registerEmail(user.getText().trim(), email.getText().trim(), emailCode.getText().trim(),
                        pwd.getText(), pwdb.getText(), key.getText().trim(), coode.getText().trim()))));
        p.add(row);
        return p;
    }

    private JPanel unbindTab() {
        JTextField user = new JTextField(18);
        JTextField pwd = new JTextField(14);
        JPanel p = formPanel();
        p.add(wrapRow("账号", user));
        p.add(wrapRow("密码", pwd));
        p.add(btnRow(btn("解绑", () -> callAsync("[jiekey.lg]", () ->
                client.unbind(user.getText().trim(), pwd.getText())))));
        return p;
    }

    private JPanel rechargeTab() {
        JTextField user = new JTextField(18);
        JTextField userpwd = new JTextField(14);
        JTextField ka = new JTextField(16);
        JTextField pwd = new JTextField(16);
        JCheckBox verifyPwd = new JCheckBox("验证登录密码(防充错)", true);
        JPanel p = formPanel();
        p.add(wrapRow("充值账号", user));
        p.add(wrapRow("登录密码", userpwd));
        p.add(wrapRow("充值卡号", ka));
        p.add(wrapRow("充值密码", pwd));
        p.add(wrapRow("是否验证密码", verifyPwd));
        p.add(btnRow(btn("充值", () -> callAsync("[chong.lg]", () ->
                client.recharge(user.getText().trim(), userpwd.getText(), verifyPwd.isSelected(), ka.getText().trim(), pwd.getText().trim())))));
        return p;
    }

    private JPanel smsResetTab() {
        JTextField mobile = new JTextField(14);
        JTextField area = new JTextField("86", 6);
        JTextField coode = new JTextField(8);
        JTextField smsCode = new JTextField(8);
        JTextField pwd = new JTextField(12);
        JTextField pwdb = new JTextField(12);
        JPanel p = formPanel();
        p.add(wrapRow("手机号", mobile));
        p.add(wrapRow("区号", area));
        p.add(wrapCaptchaRow("图形验证码", coode));
        p.add(wrapRow("短信验证码", smsCode));
        p.add(wrapRow("新密码", pwd));
        p.add(wrapRow("确认新密码", pwdb));
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 2));
        row.add(btn("发送验证码", () -> callAsync("[send_sms.lg]", () ->
                client.sendSmsCode("reset", mobile.getText().trim(), d(area, "86"), coode.getText().trim()))));
        row.add(btn("短信找回", () -> callAsync("[resetpwd_sms.lg]", () ->
                client.resetSmsPwd(mobile.getText().trim(), d(area, "86"), smsCode.getText().trim(), pwd.getText(), pwdb.getText(), coode.getText().trim()))));
        p.add(row);
        return p;
    }

    private JPanel emailResetTab() {
        JTextField email = new JTextField(20);
        JTextField coode = new JTextField(8);
        JTextField emailCode = new JTextField(10);
        JTextField pwd = new JTextField(12);
        JTextField pwdb = new JTextField(12);
        JPanel p = formPanel();
        p.add(wrapRow("邮箱", email));
        p.add(wrapCaptchaRow("图形验证码", coode));
        p.add(wrapRow("邮箱验证码", emailCode));
        p.add(wrapRow("新密码", pwd));
        p.add(wrapRow("确认新密码", pwdb));
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 2));
        row.add(btn("发送验证码", () -> callAsync("[send_email.lg]", () ->
                client.sendEmailCode("reset", email.getText().trim(), coode.getText().trim()))));
        row.add(btn("邮箱找回", () -> callAsync("[resetpwd_email.lg]", () ->
                client.resetEmailPwd(email.getText().trim(), emailCode.getText().trim(), pwd.getText(), pwdb.getText(), coode.getText().trim()))));
        p.add(row);
        return p;
    }

    private JPanel backtoTab() {
        JTextField user = new JTextField(18);
        JComboBox<String> q = new JComboBox<>(new String[]{
                "你最喜欢的颜色？", "你母亲的名字？", "你父亲的名字？", "你的出生地？", "你最喜欢的食物？", "你的小学名称？", "自定义问题"
        });
        JTextField daan = new JTextField(18);
        JTextField pwd = new JTextField(12);
        JTextField pwdb = new JTextField(12);
        JTextField coode = new JTextField(8);
        JPanel p = formPanel();
        p.add(wrapRow("账号", user));
        p.add(wrapRow("密保问题", q));
        p.add(wrapRow("密保答案", daan));
        p.add(wrapRow("新密码", pwd));
        p.add(wrapRow("确认新密码", pwdb));
        backtoCaptchaRow = wrapCaptchaRow("验证码", coode);
        p.add(backtoCaptchaRow);
        p.add(btnRow(btn("找回密码", () -> callAsync("[backto.lg]", () ->
                client.backto(user.getText().trim(), pwd.getText(), pwdb.getText(),
                        String.valueOf(q.getSelectedItem()), daan.getText().trim(), coode.getText().trim())))));
        return p;
    }

    private JPanel changePwdTab() {
        JTextField user = new JTextField(18);
        JTextField oldPwd = new JTextField(12);
        JTextField newPwd = new JTextField(12);
        JTextField newPwdb = new JTextField(12);
        JTextField img = new JTextField(8);
        JPanel p = formPanel();
        p.add(wrapRow("账号", user));
        p.add(wrapRow("旧密码", oldPwd));
        p.add(wrapRow("新密码", newPwd));
        p.add(wrapRow("确认新密码", newPwdb));
        p.add(wrapCaptchaRow("验证码", img));
        p.add(btnRow(btn("修改密码", () -> callAsync("[password.lg]", () ->
                client.changePassword(user.getText().trim(), oldPwd.getText(), newPwd.getText(), newPwdb.getText(), img.getText().trim())))));
        return p;
    }

    private JPanel feedbackTab() {
        JTextField user = new JTextField(16);
        JTextField pwd = new JTextField(12);
        JTextField title = new JTextField(18);
        JTextField qq = new JTextField(16);
        JComboBox<String> leix = new JComboBox<>(new String[]{"建议反馈", "BUG", "使用问题"});
        JTextArea txt = new JTextArea(4, 30);
        JTextField coode = new JTextField(8);
        JPanel p = formPanel();
        p.add(wrapRow("账号", user));
        p.add(wrapRow("密码", pwd));
        p.add(wrapRow("标题", title));
        p.add(wrapRow("联系", qq));
        p.add(wrapRow("类型", leix));
        p.add(wrapRow("内容", new JScrollPane(txt)));
        feedbackCaptchaRow = wrapCaptchaRow("验证码", coode);
        p.add(feedbackCaptchaRow);
        p.add(btnRow(btn("提交", () -> callAsync("[liuyan.in]", () ->
                client.feedback(user.getText().trim(), pwd.getText(), title.getText().trim(), qq.getText().trim(),
                        String.valueOf(leix.getSelectedItem()), txt.getText().trim(), coode.getText().trim())))));
        return p;
    }

    private JButton callBtn(String title, String api) {
        JButton b = new JButton(title);
        b.addActionListener(e -> callAsync("[" + api + "]", () -> client.call(api, Map.of())));
        return b;
    }

    private JPanel formPanel() {
        JPanel p = new JPanel();
        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
        p.setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 4));
        return p;
    }

    private JPanel wrapRow(String label, java.awt.Component field) {
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 0));
        JLabel l = new JLabel(label + "：");
        l.setPreferredSize(new Dimension(82, 18));
        l.setFont(UI_FONT);
        styleInput(field);
        row.add(l);
        row.add(field);
        row.setBorder(BorderFactory.createEmptyBorder(0, 0, ROW_GAP, 0));
        row.setMaximumSize(new Dimension(Integer.MAX_VALUE, INPUT_HEIGHT + ROW_GAP));
        return row;
    }

    private JPanel wrapCaptchaRow(String label, JTextField field) {
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 0));
        JLabel l = new JLabel(label + "：");
        l.setPreferredSize(new Dimension(82, 18));
        l.setFont(UI_FONT);
        styleInput(field);
        row.add(l);
        row.add(field);

        JLabel img = new JLabel("验证码加载中");
        img.setFont(UI_FONT);
        img.setPreferredSize(new Dimension(110, 32));
        img.setBorder(BorderFactory.createEtchedBorder());
        captchaLabels.add(img);
        row.add(img);

        JButton refresh = new JButton("刷新");
        refresh.setFont(UI_FONT);
        refresh.setPreferredSize(new Dimension(58, INPUT_HEIGHT));
        refresh.addActionListener(e -> refreshCaptcha(img));
        row.add(refresh);
        row.setBorder(BorderFactory.createEmptyBorder(0, 0, ROW_GAP, 0));
        row.setMaximumSize(new Dimension(Integer.MAX_VALUE, INPUT_HEIGHT + ROW_GAP));
        return row;
    }

    private void refreshAllCaptchas() {
        for (JLabel label : captchaLabels) refreshCaptcha(label);
    }

    private void refreshCaptcha(JLabel label) {
        String sessl = client.sessl();
        if (sessl == null || sessl.isEmpty()) {
            label.setIcon(null);
            label.setText("请先连接");
            return;
        }
        label.setText("加载中...");
        label.setIcon(null);
        new SwingWorker<Void, Void>() {
            private javax.swing.ImageIcon icon;
            private String err;
            @Override
            protected Void doInBackground() {
                try {
                    String u = AccountBSPHPConfig.CODE_URL_PREFIX + sessl + "&_=" + System.currentTimeMillis();
                    java.awt.Image raw = new javax.swing.ImageIcon(new URL(u)).getImage();
                    java.awt.Image scaled = raw.getScaledInstance(110, 32, java.awt.Image.SCALE_SMOOTH);
                    icon = new javax.swing.ImageIcon(scaled);
                } catch (Exception e) {
                    err = e.getMessage();
                }
                return null;
            }
            @Override
            protected void done() {
                if (icon != null) {
                    label.setText("");
                    label.setIcon(icon);
                } else {
                    label.setIcon(null);
                    label.setText("加载失败");
                    status.setText("验证码加载失败: " + (err == null ? "-" : err));
                }
            }
        }.execute();
    }

    private JPanel btnRow(JButton b) {
        b.setFont(UI_FONT);
        b.setPreferredSize(new Dimension(96, INPUT_HEIGHT));
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 0));
        row.add(b);
        return row;
    }

    private JButton btn(String title, Runnable r) {
        JButton b = new JButton(title);
        b.setFont(UI_FONT);
        b.setPreferredSize(new Dimension(96, INPUT_HEIGHT));
        b.addActionListener(e -> r.run());
        return b;
    }

    private void styleInput(java.awt.Component c) {
        c.setFont(UI_FONT);
        if (c instanceof JTextField tf) {
            Dimension d = tf.getPreferredSize();
            tf.setPreferredSize(new Dimension(Math.max(d.width, 110), INPUT_HEIGHT));
        } else if (c instanceof JComboBox<?> cb) {
            Dimension d = cb.getPreferredSize();
            cb.setPreferredSize(new Dimension(Math.max(d.width, 150), INPUT_HEIGHT));
        } else if (c instanceof JScrollPane sp) {
            Dimension d = sp.getPreferredSize();
            sp.setPreferredSize(new Dimension(Math.max(d.width, 280), 78));
        } else if (c instanceof JCheckBox chk) {
            Dimension d = chk.getPreferredSize();
            chk.setPreferredSize(new Dimension(d.width + 6, INPUT_HEIGHT));
        }
    }

    @FunctionalInterface
    private interface APICall {
        JsonObject run() throws Exception;
    }

    private void callAsync(String tag, APICall c) {
        callAsync(tag, c, false);
    }

    private void callAsync(String tag, APICall c, boolean openConsoleOnSuccess) {
        new SwingWorker<Void, Void>() {
            @Override protected Void doInBackground() throws Exception {
                JsonObject r = c.run();
                String m = msg(r);
                String codeText = codeText(r);
                javax.swing.SwingUtilities.invokeLater(() -> {
                    status.setText(logLine(tag, codeText, m));
                    if (openConsoleOnSuccess) {
                        Integer codeNum = code(r);
                        if (codeNum != null && (codeNum == 1011 || codeNum == 9908)) openConsole();
                    }
                });
                return null;
            }
        }.execute();
    }

    private static String msg(JsonObject r) {
        return r == null ? "请求失败" : AccountBSPHPClient.msg(r.get("data"));
    }

    private static Integer code(JsonObject r) {
        if (r == null || !r.has("code") || r.get("code").isJsonNull()) return null;
        try { return r.get("code").getAsInt(); } catch (Exception e) { return null; }
    }

    private static String codeText(JsonObject r) {
        if (r == null || !r.has("code") || r.get("code").isJsonNull()) return "-";
        return r.get("code").getAsString();
    }

    private static String logLine(String tag, String code, String message) {
        return "[" + LocalTime.now().format(TIME_FMT) + "] " + tag + " code=" + code + " " + message;
    }

    private void openConsole() {
        if (consoleDialog != null && consoleDialog.isDisplayable()) {
            consoleDialog.setVisible(true);
            consoleDialog.toFront();
            return;
        }
        consoleDialog = new ConsoleDialog(this, client);
        consoleDialog.setVisible(true);
    }

    private static String d(JTextField f, String def) {
        String v = f.getText().trim();
        return v.isEmpty() ? def : v;
    }

    private void initBootstrap() {
        new SwingWorker<Void, Void>() {
            @Override protected Void doInBackground() {
                try {
                    if (client.bootstrap()) {
                        JsonObject n = client.call("gg.in", java.util.Map.of());
                        String text = n == null ? "公告获取失败" : AccountBSPHPClient.msg(n.get("data"));
                        javax.swing.SwingUtilities.invokeLater(() -> {
                            notice.setText(text == null || text.isEmpty() ? "暂无公告" : text);
                            status.setText("服务已连接");
                            applyCaptchaVisibility();
                            refreshAllCaptchas();
                        });
                        fetchCaptchaVisibility();
                    } else {
                        javax.swing.SwingUtilities.invokeLater(() -> status.setText("初始化失败"));
                    }
                } catch (Exception e) {
                    javax.swing.SwingUtilities.invokeLater(() -> status.setText("初始化失败: " + e.getMessage()));
                }
                return null;
            }
        }.execute();
    }

    private void fetchCaptchaVisibility() {
        try {
            JsonObject all = client.getCodeEnabled();
            if (all != null && all.has("data") && !all.get("data").isJsonNull()) {
                String data = all.get("data").getAsString();
                String[] parts = data.split("\\|", -1);
                if (parts.length > 0) loginCodeEnabled = "checked".equalsIgnoreCase(parts[0].trim());
                if (parts.length > 2) backtoCodeEnabled = "checked".equalsIgnoreCase(parts[2].trim());
                if (parts.length > 3) feedbackCodeEnabled = "checked".equalsIgnoreCase(parts[3].trim());
            } else {
                loginCodeEnabled = isChecked(client.getCodeEnabled(TYPE_LOGIN));
                backtoCodeEnabled = isChecked(client.getCodeEnabled(TYPE_BACKTO));
                feedbackCodeEnabled = isChecked(client.getCodeEnabled(TYPE_FEEDBACK));
            }
            javax.swing.SwingUtilities.invokeLater(this::applyCaptchaVisibility);
        } catch (Exception ignored) {
            // Keep default visible when API query fails.
        }
    }

    private boolean isChecked(JsonObject r) {
        if (r == null || !r.has("data") || r.get("data").isJsonNull()) return true;
        return "checked".equalsIgnoreCase(r.get("data").getAsString().trim());
    }

    private void applyCaptchaVisibility() {
        if (loginCaptchaRow != null) loginCaptchaRow.setVisible(loginCodeEnabled);
        if (backtoCaptchaRow != null) backtoCaptchaRow.setVisible(backtoCodeEnabled);
        if (feedbackCaptchaRow != null) feedbackCaptchaRow.setVisible(feedbackCodeEnabled);
        revalidate();
        repaint();
    }
}
