from __future__ import annotations

from collections import deque
from dataclasses import dataclass
from pathlib import Path
from typing import Callable

from PySide6.QtCore import Qt, QTimer, QSize
from PySide6.QtGui import QGuiApplication, QIcon, QPixmap
from PySide6.QtWidgets import (
    QApplication,
    QButtonGroup,
    QCheckBox,
    QDialog,
    QDialogButtonBox,
    QFormLayout,
    QFrame,
    QGridLayout,
    QGroupBox,
    QHBoxLayout,
    QHeaderView,
    QLabel,
    QLineEdit,
    QMainWindow,
    QMessageBox,
    QPushButton,
    QRadioButton,
    QSizePolicy,
    QStackedWidget,
    QStatusBar,
    QTableWidget,
    QTableWidgetItem,
    QTextEdit,
    QToolButton,
    QVBoxLayout,
    QWidget,
)


CLIENT_DIR = Path(__file__).resolve().parents[2]
ASSET_DIR = CLIENT_DIR / "source" / "assets"
APP_ICON = ASSET_DIR / "app_icon_wawa.ico"
DIALOG_ICON = ASSET_DIR / "app_dialog_icon_wawa.ico"


@dataclass(frozen=True)
class CloudConfig:
    key: str
    title: str
    sidebar_text: str
    icon_name: str
    regions: tuple[str, ...]
    manage_summary: str
    purchase_summary: str
    has_ros: bool = False
    has_nic: bool = False

    @property
    def icon_path(self) -> Path:
        return ASSET_DIR / self.icon_name


CLOUDS: tuple[CloudConfig, ...] = (
    CloudConfig(
        key="huawei",
        title="华为云管理",
        sidebar_text="华为云管理",
        icon_name="logo_huawei_45.png",
        regions=("北京", "上海", "广州", "贵阳", "乌兰察布", "华东", "青岛"),
        manage_summary="ECS:0/EIP:0/VPC:0/子网:0/安全组:0",
        purchase_summary="ECS:0 EIP:0",
        has_ros=True,
    ),
    CloudConfig(
        key="ctyun",
        title="天翼云管理",
        sidebar_text="天翼云管理",
        icon_name="logo_ctyun_45.png",
        regions=("芜湖2", "芜湖3"),
        manage_summary="ECS:0/EIP:0/VPC:0/子网:0/安全组:0",
        purchase_summary="ECS:0/EIP:0/VPC:0/子网:0/安全组:0",
    ),
    CloudConfig(
        key="volc",
        title="火山引擎管理",
        sidebar_text="火山引擎管理",
        icon_name="logo_volc_45.png",
        regions=("北京", "上海", "广州"),
        manage_summary="ECS:0/EIP:0/VPC:0/子网:0/网卡:0/安全组:0",
        purchase_summary="ECS:0 EIP:0",
        has_nic=True,
    ),
)


STYLE = """
QWidget {
    background: #f5f6f8;
    color: #111827;
    font-family: "Microsoft YaHei UI", "Microsoft YaHei", "Segoe UI";
    font-size: 14px;
}
QLineEdit, QTextEdit, QTableWidget {
    background: #ffffff;
    border: 1px solid #cfd6df;
}
QPushButton, QToolButton {
    background: #ffffff;
    border: 1px solid #cfd6df;
    padding: 7px 14px;
}
QPushButton:hover, QToolButton:hover {
    background: #f0f5ff;
    border-color: #9db9e8;
}
QPushButton#tabButton {
    min-width: 118px;
    min-height: 52px;
    font-size: 20px;
    font-weight: 700;
    border: 1px solid #cfd6df;
    border-bottom: 1px solid #cfd6df;
    padding: 0 8px 1px 8px;
}
QPushButton#tabButton[active="true"] {
    border-bottom: 2px solid #111827;
}
QGroupBox {
    border: 1px solid #d7dce3;
    margin-top: 14px;
    padding: 12px 10px 10px 10px;
}
QGroupBox::title {
    subcontrol-origin: margin;
    left: 10px;
    padding: 0 4px;
}
QHeaderView::section {
    background: #ffffff;
    border: 0;
    border-right: 1px solid #e1e5ea;
    padding: 5px;
    font-weight: 700;
}
QTableWidget {
    gridline-color: #edf0f4;
    selection-background-color: #1f6feb;
    selection-color: #ffffff;
    alternate-background-color: #fafafa;
}
QStatusBar {
    background: #ffffff;
    border-top: 1px solid #d9dee6;
}
QFrame#sidebar {
    background: #eef1f5;
    border-right: 1px solid #d4dae2;
}
QToolButton#cloudButton {
    background: transparent;
    border: 0;
    padding: 8px 4px;
    font-weight: 700;
}
QToolButton#cloudButton[active="true"] {
    background: #ffffff;
    border-left: 3px solid #ef1025;
}
QLabel#cloudTitle {
    font-size: 24px;
    font-weight: 800;
}
QLabel#sectionTitle {
    font-weight: 700;
}
"""


def icon_for(path: Path) -> QIcon:
    if path.exists():
        return QIcon(str(path))
    return QIcon()


def apply_active_property(widget: QWidget, active: bool) -> None:
    widget.setProperty("active", active)
    widget.style().unpolish(widget)
    widget.style().polish(widget)


class CustomConnectionDialog(QDialog):
    def __init__(self, cloud_title: str, parent: QWidget | None = None, ros: bool = False) -> None:
        super().__init__(parent)
        if ros:
            self.setWindowTitle(f"{cloud_title}-Ros镜像搭建(自定义连接配置)")
        else:
            self.setWindowTitle(f"{cloud_title}-自定义连接配置")
        self.setWindowIcon(icon_for(DIALOG_ICON))
        self.setModal(True)
        self.setMinimumWidth(420)
        self.setSizeGripEnabled(False)

        layout = QVBoxLayout(self)
        form = QFormLayout()
        form.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)
        form.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)
        form.setHorizontalSpacing(10)
        form.setVerticalSpacing(10)

        if ros:
            self.password = QLineEdit("wawacloud")
            self.license_account = QLineEdit()
            self.license_password = QLineEdit()
            form.addRow("默认连接密码", self.password)
            self.once = QCheckBox("生效1次")
            form.addRow("", self.once)
            form.addRow("许可证账号", self.license_account)
            form.addRow("许可证密码", self.license_password)
            layout.addLayout(form)
            warning = QLabel("许可证若未失效请慎重修改！")
            layout.addWidget(warning)
        else:
            self.username = QLineEdit("root")
            self.password = QLineEdit("wawacloud")
            self.port = QLineEdit("23456")
            form.addRow("Socks5用户名", self.username)
            form.addRow("密码", self.password)
            form.addRow("Socks5端口", self.port)
            self.once = QCheckBox("生效1次")
            form.addRow("", self.once)
            layout.addLayout(form)

        self.once.setChecked(False)

        buttons = QDialogButtonBox()
        save = buttons.addButton("保存", QDialogButtonBox.AcceptRole)
        cancel = buttons.addButton("取消", QDialogButtonBox.RejectRole)
        save.setMinimumWidth(112)
        cancel.setMinimumWidth(112)
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        layout.addWidget(buttons)

    def summary(self) -> str:
        if hasattr(self, "port"):
            return f"Socks5用户名：{self.username.text()}，密码：{self.password.text()}，Socks5端口：{self.port.text()}"
        return f"默认连接密码：{self.password.text()}"


class TaskQueue:
    def __init__(self, status_callback: Callable[[str], None]) -> None:
        self.current: str | None = None
        self.pending: deque[tuple[str, Callable[[], None]]] = deque()
        self.status_callback = status_callback

    def add_or_run(self, name: str, action: Callable[[], None], parent: QWidget) -> None:
        if self.current is None:
            self._start(name, action)
            return

        if len(self.pending) >= 6:
            QMessageBox.warning(parent, "多云管理-任务队列", "当前云任务队列已满，最多可排队6个任务。")
            return

        ok = QMessageBox.question(
            parent,
            "多云管理-任务队列",
            f"当前任务正在执行：{self.current}\n\n是否将该任务加入等待队列？\n{name}",
        )
        if ok == QMessageBox.Yes:
            self.pending.append((name, action))
            QMessageBox.information(parent, "多云管理-任务队列", "任务已进入队列，当前任务完成后会按顺序自动执行。")
            self._emit_status()

    def _start(self, name: str, action: Callable[[], None]) -> None:
        self.current = name
        self._emit_status()
        action()
        QTimer.singleShot(1200, self.finish_current)

    def finish_current(self) -> None:
        finished = self.current
        self.current = None
        if finished:
            self.status_callback(f"任务完成：{finished}")
        if self.pending:
            name, action = self.pending.popleft()
            self._start(name, action)
        else:
            self._emit_status()

    def _emit_status(self) -> None:
        current = self.current or "无"
        pending = "、".join(name for name, _ in self.pending) or "无"
        self.status_callback(f"当前任务：{current}，后续任务：{pending}")


class ResultPanel(QGroupBox):
    def __init__(self, title: str, parent: QWidget | None = None) -> None:
        super().__init__(title, parent)
        layout = QVBoxLayout(self)
        button_row = QHBoxLayout()
        button_row.addStretch(1)
        self.copy_plain = QPushButton(f"复制纯{title}")
        self.copy_all = QPushButton(f"复制{title}")
        button_row.addWidget(self.copy_plain)
        button_row.addWidget(self.copy_all)
        layout.addLayout(button_row)
        self.text = QTextEdit()
        self.text.setAcceptRichText(False)
        self.text.setLineWrapMode(QTextEdit.NoWrap)
        layout.addWidget(self.text, 1)
        self.copy_plain.clicked.connect(self.copy_plain_text)
        self.copy_all.clicked.connect(self.copy_all_text)

    def set_template(self, region: str, rows: list[str]) -> None:
        if self.title().lower().startswith("l2"):
            header = "入口IP/账号/密码/密钥/对应出口IP"
            disclaimer = "L2TP丨免责声明：仅用于合法合规的网络技术交流用途。"
        else:
            header = "IP/端口/用户名/密码"
            disclaimer = "Socks5丨免责声明：仅用于合法合规的网络技术交流用途。"
        body = "\n".join([region, header, *rows, "======================================", disclaimer, "使用者需自行承担一切行为责任。", "用户名"])
        self.text.setPlainText(body)

    def copy_plain_text(self) -> None:
        lines = [line for line in self.text.toPlainText().splitlines() if "/" in line and "免责声明" not in line]
        QApplication.clipboard().setText("\n".join(lines))

    def copy_all_text(self) -> None:
        QApplication.clipboard().setText(self.text.toPlainText())


class CloudPage(QWidget):
    def __init__(self, config: CloudConfig, queue: TaskQueue, status_callback: Callable[[str], None]) -> None:
        super().__init__()
        self.config = config
        self.queue = queue
        self.status_callback = status_callback
        self.custom_config = "默认"
        self.ros_config = "默认"

        root = QVBoxLayout(self)
        root.setContentsMargins(14, 12, 14, 8)
        root.setSpacing(10)

        root.addLayout(self._build_header())

        tab_row = QHBoxLayout()
        self.purchase_tab = self._tab_button("购买搭建", 0)
        self.manage_tab = self._tab_button("资源管理", 1)
        tab_row.addWidget(self.purchase_tab)
        tab_row.addWidget(self.manage_tab)
        tab_row.addStretch(1)
        root.addLayout(tab_row)

        self.stack = QStackedWidget()
        self.purchase_page = self._build_purchase_page()
        self.manage_page = self._build_manage_page()
        self.stack.addWidget(self.purchase_page)
        self.stack.addWidget(self.manage_page)
        root.addWidget(self.stack, 1)
        self.set_tab(0)

    def _build_header(self) -> QLayout:
        header = QGridLayout()
        header.setColumnStretch(0, 1)
        header.setColumnStretch(4, 2)
        title_box = QVBoxLayout()
        title = QLabel(self.config.title)
        title.setObjectName("cloudTitle")
        subtitle = QLabel("购买搭建、资源查询、重装系统、退订/删除资源")
        title_box.addWidget(title)
        title_box.addWidget(subtitle)
        header.addLayout(title_box, 0, 0, 3, 1)

        minus = QPushButton("-")
        number = QLineEdit("1")
        number.setAlignment(Qt.AlignCenter)
        number.setFixedWidth(34)
        plus = QPushButton("+")
        clone = QPushButton("新增窗口")
        for button in (minus, plus):
            button.setFixedWidth(32)
        counter = QHBoxLayout()
        counter.addWidget(minus)
        counter.addWidget(number)
        counter.addWidget(plus)
        header.addLayout(counter, 0, 2)
        header.addWidget(clone, 1, 2)

        self.ak_input = QLineEdit()
        self.sk_input = QLineEdit()
        self.mark_input = QLineEdit()
        form = QGridLayout()
        form.addWidget(QLabel("AccessKey ID"), 0, 0)
        form.addWidget(self.ak_input, 0, 1)
        form.addWidget(QLabel("SecurityKey"), 1, 0)
        form.addWidget(self.sk_input, 1, 1)
        form.addWidget(QLabel("User(可编辑)"), 2, 0)
        form.addWidget(self.mark_input, 2, 1)
        header.addLayout(form, 0, 4, 3, 1)

        paste = QPushButton("识别\n粘贴")
        paste.setMinimumWidth(76)
        paste.setMinimumHeight(84)
        header.addWidget(paste, 0, 5, 3, 1)
        paste.clicked.connect(self._paste_credentials)
        return header

    def _tab_button(self, text: str, index: int) -> QPushButton:
        button = QPushButton(text)
        button.setObjectName("tabButton")
        button.clicked.connect(lambda: self.set_tab(index))
        return button

    def set_tab(self, index: int) -> None:
        self.stack.setCurrentIndex(index)
        apply_active_property(self.purchase_tab, index == 0)
        apply_active_property(self.manage_tab, index == 1)

    def _paste_credentials(self) -> None:
        text = QApplication.clipboard().text().strip()
        parts = [part.strip() for part in text.replace("\t", "\n").splitlines() if part.strip()]
        if parts:
            self.ak_input.setText(parts[0])
        if len(parts) > 1:
            self.sk_input.setText(parts[1])
        if len(parts) > 2:
            self.mark_input.setText(parts[2])
        self.append_log(f"{self.config.title.replace('管理', '')}查询AK/SK中，正在校验合作伙伴归属，请稍等。")

    def _build_purchase_page(self) -> QWidget:
        page = QWidget()
        outer = QHBoxLayout(page)
        outer.setContentsMargins(0, 0, 0, 0)
        outer.setSpacing(12)

        main = QVBoxLayout()
        main.addWidget(self._build_task_options())
        main.addLayout(self._build_purchase_actions())
        self.purchase_table = self._table(("选择", "地区", "状态", "资源", "服务器IP", "规格", "到期时间"))
        self._fill_purchase_table(unread=True)
        main.addWidget(self.purchase_table, 3)
        main.addLayout(self._build_region_actions())

        result_row = QHBoxLayout()
        self.l2tp = ResultPanel("L2TP")
        self.socks = ResultPanel("Socks5")
        result_row.addWidget(self.l2tp, 1)
        result_row.addWidget(self.socks, 1)
        main.addLayout(result_row, 2)
        outer.addLayout(main, 4)

        outer.addWidget(self._build_log_panel(), 1)
        return page

    def _build_manage_page(self) -> QWidget:
        page = QWidget()
        outer = QHBoxLayout(page)
        outer.setContentsMargins(0, 0, 0, 0)
        outer.setSpacing(12)
        main = QVBoxLayout()

        action_row = QHBoxLayout()
        read = QPushButton("一键读取账号资源")
        clear = QPushButton("一键清空账号资源")
        rebuild = QPushButton("重装选定云服务器")
        action_row.addWidget(read)
        action_row.addStretch(1)
        action_row.addWidget(clear)
        action_row.addWidget(rebuild)
        main.addLayout(action_row)

        self.manage_table = self._table(("地区", "✓", "类型", "名称/IP", "状态", "到期时间", "规格"))
        self._fill_manage_table(empty=True)
        main.addWidget(self.manage_table, 1)

        bottom = QHBoxLayout()
        for text in ("全选资源", "全选云服务器", "全选IP", "全选网络资源", "取消选择"):
            bottom.addWidget(QPushButton(text))
        bottom.addStretch(1)
        bottom.addWidget(QPushButton("退订/删除选定资源"))
        main.addLayout(bottom)
        outer.addLayout(main, 4)
        outer.addWidget(self._build_log_panel(), 1)

        read.clicked.connect(lambda: self.enqueue("读取账号资源", self._read_resources))
        clear.clicked.connect(lambda: self.enqueue("一键清空账号资源", lambda: self.append_log("已加入清空资源流程。")))
        rebuild.clicked.connect(lambda: self.enqueue("重装选定云服务器", lambda: self.append_log("已加入重装流程，结果框会清除失效地区。")))
        return page

    def _build_task_options(self) -> QGroupBox:
        box = QGroupBox("任务选项")
        layout = QGridLayout(box)
        layout.setHorizontalSpacing(26)
        layout.setVerticalSpacing(8)

        if self.config.key == "huawei":
            layout.addWidget(QLabel("购买\n选项"), 0, 0, 2, 1)
            specs = [QRadioButton("1vCPUs|1GiB"), QRadioButton("20(1vCPUs|1GiB)"), QRadioButton("2vCPUs|2GiB")]
            specs[0].setChecked(True)
            for radio in specs:
                self._purchase_group().addButton(radio)
            layout.addWidget(specs[0], 0, 1)
            layout.addWidget(specs[1], 0, 2)
            layout.addWidget(specs[2], 1, 1)
            layout.addWidget(QCheckBox("静态BGP(北上广贵)"), 1, 2)
            on_demand = QCheckBox("按需")
            on_demand.setChecked(False)
            layout.addWidget(on_demand, 1, 4, alignment=Qt.AlignRight | Qt.AlignBottom)
            self._build_option_radios(layout, start_col=5)
        elif self.config.key == "volc":
            layout.addWidget(QLabel("购买\n选项"), 0, 0, 2, 1)
            specs = [QRadioButton("2vCPUs|2GiB"), QRadioButton("2vCPUs|4GiB")]
            specs[0].setChecked(True)
            for radio in specs:
                self._purchase_group().addButton(radio)
            layout.addWidget(specs[0], 0, 1)
            layout.addWidget(specs[1], 1, 1)
            on_demand = QCheckBox("按需")
            on_demand.setChecked(False)
            layout.addWidget(on_demand, 1, 2, alignment=Qt.AlignRight | Qt.AlignBottom)
            layout.addWidget(QLabel("便捷\n数量"), 0, 3, 2, 1)
            layout.addWidget(QCheckBox("满配(24)"), 0, 4)
            layout.addWidget(QCheckBox("大满配(48)"), 1, 4)
            self._build_option_radios(layout, start_col=6)
        else:
            layout.addWidget(QLabel("300M地区"), 0, 0)
            for i, region in enumerate(("芜湖2", "芜湖3")):
                layout.addWidget(QCheckBox(region), i + 1, 0)
            separator = QFrame()
            separator.setFrameShape(QFrame.VLine)
            separator.setFrameShadow(QFrame.Sunken)
            layout.addWidget(separator, 0, 2, 3, 1)
            layout.addWidget(QLabel("60M地区"), 0, 3)
            layout.addWidget(QCheckBox("保留扩展位"), 1, 3)
            self._build_option_radios(layout, start_col=5, ros=False)

        layout.setColumnStretch(layout.columnCount(), 1)
        return box

    def _purchase_group(self) -> QButtonGroup:
        if not hasattr(self, "_purchase_buttons"):
            self._purchase_buttons = QButtonGroup(self)
        return self._purchase_buttons

    def _build_option_radios(self, layout: QGridLayout, start_col: int, ros: bool | None = None) -> None:
        layout.addWidget(QLabel("搭建\n选项"), 0, start_col, 2, 1)
        build_group = QButtonGroup(self)
        random = QRadioButton("随机连接配置")
        default = QRadioButton("默认连接配置(右键配置)")
        random.setChecked(True)
        build_group.addButton(random)
        build_group.addButton(default)
        default.setContextMenuPolicy(Qt.CustomContextMenu)
        default.customContextMenuRequested.connect(lambda _pos: self.open_custom_dialog(ros=False))
        layout.addWidget(random, 0, start_col + 1)
        layout.addWidget(default, 1, start_col + 1)
        if ros is not False and self.config.has_ros:
            ros_radio = QCheckBox("Ros镜像搭建(右键配置|L2TP)")
            ros_radio.setContextMenuPolicy(Qt.CustomContextMenu)
            ros_radio.customContextMenuRequested.connect(lambda _pos: self.open_custom_dialog(ros=True))
            layout.addWidget(ros_radio, 1, start_col + 2)

    def _build_purchase_actions(self) -> QLayout:
        row = QHBoxLayout()
        read = QPushButton("读取账号资源")
        rebuild = QPushButton("重装选定云服务器")
        row.addWidget(read)
        row.addStretch(1)
        row.addWidget(rebuild)
        read.clicked.connect(lambda: self.enqueue("读取账号资源", self._read_resources))
        rebuild.clicked.connect(lambda: self.enqueue("重装选定云服务器", lambda: self.append_log("重装完成后，该地区旧结果不会继续显示。")))
        return row

    def _build_region_actions(self) -> QLayout:
        row = QHBoxLayout()
        for text, task in (
            ("全选地区", None),
            ("取消选择", None),
            ("购买选择地区", "购买选择地区"),
            ("搭建选择地区", "搭建选择地区"),
        ):
            button = QPushButton(text)
            if task:
                button.clicked.connect(lambda _checked=False, t=task: self.enqueue(t, lambda: self._simulate_build_result(t)))
            row.addWidget(button)
        row.addStretch(1)
        one_click = QPushButton("一键购买并搭建选择地区")
        one_click.clicked.connect(lambda: self.enqueue("一键购买并搭建选择地区", lambda: self._simulate_build_result("一键购买并搭建")))
        row.addWidget(one_click)
        return row

    def _build_log_panel(self) -> QGroupBox:
        box = QGroupBox("实时日志")
        layout = QVBoxLayout(box)
        self.log = QTextEdit()
        self.log.setAcceptRichText(False)
        self.log.setLineWrapMode(QTextEdit.WidgetWidth)
        layout.addWidget(self.log, 1)
        buttons = QHBoxLayout()
        export = QPushButton("导出日志")
        stop = QPushButton("停止任务")
        buttons.addWidget(export)
        buttons.addStretch(1)
        buttons.addWidget(stop)
        layout.addLayout(buttons)
        export.clicked.connect(lambda: QMessageBox.information(self, f"{self.config.title}-导出日志", "Qt试验版已准备导出入口，正式接入业务后会写入日志文件。"))
        stop.clicked.connect(lambda: self.append_log("已请求停止当前任务。"))
        return box

    def _table(self, headers: tuple[str, ...]) -> QTableWidget:
        table = QTableWidget(0, len(headers))
        table.setHorizontalHeaderLabels(headers)
        table.setAlternatingRowColors(True)
        table.setSelectionBehavior(QTableWidget.SelectRows)
        table.setEditTriggers(QTableWidget.NoEditTriggers)
        table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        table.verticalHeader().setVisible(False)
        table.itemClicked.connect(self._copy_ip_when_clicked)
        return table

    def _fill_purchase_table(self, unread: bool = False) -> None:
        table = self.purchase_table
        table.setRowCount(0)
        for region in self.config.regions:
            row = table.rowCount()
            table.insertRow(row)
            values = ("□", region, "未读取" if unread else "使用中", self.config.purchase_summary, "", "", "")
            for col, value in enumerate(values):
                table.setItem(row, col, QTableWidgetItem(value))
        table.resizeColumnsToContents()

    def _fill_manage_table(self, empty: bool = False) -> None:
        table = self.manage_table
        table.setRowCount(0)
        if empty:
            return
        types = ["ECS", "EIP", "VPC", "子网"]
        if self.config.has_nic:
            types.append("网卡")
        types.append("安全组")
        for resource_type in types:
            row = table.rowCount()
            table.insertRow(row)
            values = (self.config.regions[0], "□", resource_type, f"{resource_type.lower()}-demo", "使用中", "按量付费", "默认")
            for col, value in enumerate(values):
                table.setItem(row, col, QTableWidgetItem(value))
        table.resizeColumnsToContents()

    def _read_resources(self) -> None:
        self.append_log(f"{self.config.title.replace('管理', '')}查询AK/SK中，正在校验合作伙伴归属，请稍等。")
        QTimer.singleShot(450, lambda: self.append_log("您好合作伙伴，欢迎使用！账号资源读取完毕。"))
        QTimer.singleShot(450, lambda: self.status_callback(f"{self.config.title} | 您好合作伙伴，欢迎使用！账号资源读取完毕。"))
        QTimer.singleShot(450, lambda: self._fill_purchase_table(unread=False))
        QTimer.singleShot(450, lambda: self._fill_manage_table(empty=False))

    def _simulate_build_result(self, name: str) -> None:
        region = self.config.regions[0]
        self.append_log(f"{self.config.title}开始执行：{name}。")
        l2tp_rows = [
            "113.45.19.51/user1/wawacloud/wawacloud/113.45.19.51",
            "113.45.19.51/user2/wawacloud/wawacloud/139.159.249.125",
        ]
        socks_rows = [
            "113.45.19.51/23456/root/wawacloud",
            "139.159.249.125/23456/root/wawacloud",
        ]
        self.l2tp.set_template(region, l2tp_rows)
        self.socks.set_template(region, socks_rows)
        self.append_log(f"{self.config.title}{name}任务结束，成功1/1个地区。")

    def enqueue(self, name: str, action: Callable[[], None]) -> None:
        cloud_task_name = f"{self.config.title}-{name}"
        self.queue.add_or_run(cloud_task_name, action, self)

    def open_custom_dialog(self, ros: bool = False) -> None:
        if not self.ak_input.text().strip() or not self.sk_input.text().strip():
            QMessageBox.information(self, f"{self.config.title}-自定义连接配置", "请先填写并通过AK/SK合作伙伴校验后再打开连接配置。")
            return
        dialog = CustomConnectionDialog(self.config.title, self, ros=ros)
        if dialog.exec() == QDialog.Accepted:
            if ros:
                self.ros_config = dialog.summary()
            else:
                self.custom_config = dialog.summary()
            self.append_log(f"已保存{dialog.windowTitle()}：{dialog.summary()}。")

    def append_log(self, text: str) -> None:
        self.log.append(text)
        self.log.moveCursor(self.log.textCursor().End)

    def _copy_ip_when_clicked(self, item: QTableWidgetItem) -> None:
        value = item.text().strip()
        if value.count(".") == 3 and all(part.isdigit() for part in value.split(".") if part):
            QApplication.clipboard().setText(value)
            self.status_callback(f"已复制IP：{value}")


class MultiCloudWindow(QMainWindow):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("多云管理")
        self.setWindowIcon(icon_for(APP_ICON))
        self.resize(1440, 900)

        central = QWidget()
        root = QHBoxLayout(central)
        root.setContentsMargins(0, 0, 0, 0)
        root.setSpacing(0)

        self.stack = QStackedWidget()
        self.status = QStatusBar()
        self.setStatusBar(self.status)
        self.queue = TaskQueue(self.set_status)

        root.addWidget(self._build_sidebar(), 0)
        for config in CLOUDS:
            self.stack.addWidget(CloudPage(config, self.queue, self.set_status))
        root.addWidget(self.stack, 1)
        self.setCentralWidget(central)
        self.set_status("多云管理 | 就绪")
        self._select_cloud(0)

    def _build_sidebar(self) -> QWidget:
        sidebar = QFrame()
        sidebar.setObjectName("sidebar")
        sidebar.setFixedWidth(100)
        layout = QVBoxLayout(sidebar)
        layout.setContentsMargins(6, 18, 6, 8)
        layout.setSpacing(22)
        self.cloud_buttons: list[QToolButton] = []
        layout.addStretch(1)
        for index, config in enumerate(CLOUDS):
            button = QToolButton()
            button.setObjectName("cloudButton")
            button.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
            button.setIcon(icon_for(config.icon_path))
            button.setIconSize(QSize(45, 45))
            button.setText(config.sidebar_text)
            button.clicked.connect(lambda _checked=False, i=index: self._select_cloud(i))
            self.cloud_buttons.append(button)
            layout.addWidget(button)
        layout.addStretch(2)
        menu = QToolButton()
        menu.setText("☰")
        menu.setToolButtonStyle(Qt.ToolButtonTextOnly)
        layout.addWidget(menu)
        return sidebar

    def _select_cloud(self, index: int) -> None:
        self.stack.setCurrentIndex(index)
        for i, button in enumerate(self.cloud_buttons):
            apply_active_property(button, i == index)
        self.set_status(f"{CLOUDS[index].title} | 就绪")

    def set_status(self, text: str) -> None:
        self.status.showMessage(text)


def self_test() -> int:
    missing = [str(path) for path in (APP_ICON, DIALOG_ICON, *(cloud.icon_path for cloud in CLOUDS)) if not path.exists()]
    if missing:
        print("Missing assets:")
        for path in missing:
            print(path)
        return 1
    print("Qt preview self-test ok.")
    print(f"Cloud pages: {', '.join(cloud.title for cloud in CLOUDS)}")
    return 0


def run_app() -> int:
    app = QApplication([])
    app.setStyle("Fusion")
    app.setApplicationName("多云管理")
    app.setWindowIcon(icon_for(APP_ICON))
    app.setStyleSheet(STYLE)
    window = MultiCloudWindow()
    screen = QGuiApplication.primaryScreen()
    if screen is not None:
        window.setGeometry(screen.availableGeometry())
    window.showMaximized()
    return app.exec()
