"""
settings_module.py — Cài Đặt hệ thống & thông tin công ty
"""
import os
import sys
from PySide6.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QLabel, QFrame,
    QLineEdit, QPushButton, QGroupBox, QGridLayout,
    QScrollArea, QMessageBox, QTabWidget, QApplication, QTextEdit, QInputDialog, QFileDialog, QComboBox, QCheckBox
)
from PySide6.QtCore import Qt
from PySide6.QtGui import QColor, QFont, QLinearGradient, QPainter, QPainterPath, QPen, QPixmap

from gui.theme import COLORS
from gui.ui_helpers import show_toast, PrimaryButton, SecondaryButton
from core.utils import build_contract_software_guide_block, get_app_data_path, get_resource_path


class SettingsPage(QWidget):
    """Module Cài Đặt."""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setObjectName("content_area")

        root = QVBoxLayout(self)
        root.setContentsMargins(0, 0, 0, 0)
        root.setSpacing(0)

        # ── Tabs ───────────────────────────────────────────
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        scroll.setFrameShape(QFrame.Shape.NoFrame)

        body = QWidget()
        body_layout = QVBoxLayout(body)
        body_layout.setContentsMargins(16, 20, 16, 20)
        body_layout.setSpacing(20)

        self.tabs = QTabWidget()
        self.tabs.addTab(self._build_company_tab(), "🏪  Thông tin Công ty / HKD")
        self.tabs.addTab(self._build_contract_config_tab(), "🖋️  Cấu hình Hợp đồng")
        self.tabs.addTab(self._build_system_tab(),  "🖥️  Hệ thống")
        self.tabs.addTab(self._build_activation_tab(), "🔐  Bản quyền")
        body_layout.addWidget(self.tabs)

        # Cho phép mở tab Bản quyền kể cả khi thông tin công ty chưa nhập đủ,
        # để người dùng có thể xem trạng thái và nhập license key trước.

        body_layout.addStretch()

        scroll.setWidget(body)
        root.addWidget(scroll)

    def _make_field_row(self, layout, row, label, placeholder, value="", suffix_btn=None):
        lbl = QLabel(label + ":")
        lbl.setFont(QFont("Segoe UI", 10))
        lbl.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)

        inp = QLineEdit(value)
        inp.setPlaceholderText(placeholder)
        inp.setFixedHeight(36)

        layout.addWidget(lbl, row, 0, 1, 1)
        if suffix_btn:
            h = QHBoxLayout()
            h.addWidget(inp)
            h.addWidget(suffix_btn)
            layout.addLayout(h, row, 1, 1, 1)
        else:
            layout.addWidget(inp, row, 1, 1, 1)
        return inp

    def _build_company_tab(self) -> QWidget:
        w = QWidget()
        outer = QVBoxLayout(w)
        outer.setContentsMargins(0, 15, 0, 0)
        outer.setSpacing(20)

        profile_box = QGroupBox("Danh sách MST đang quản lý")
        profile_grid = QGridLayout(profile_box)
        profile_grid.setContentsMargins(20, 20, 20, 20)
        profile_grid.setColumnStretch(1, 1)
        profile_grid.setSpacing(12)

        lbl_profiles = QLabel("MST đã lưu:")
        lbl_profiles.setFont(QFont("Segoe UI", 10))
        lbl_profiles.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)

        self.cbo_company_profiles = QComboBox()
        self.cbo_company_profiles.setFixedHeight(36)
        self.cbo_company_profiles.setMinimumWidth(420)

        btn_load_company = SecondaryButton("Nạp MST")
        btn_load_company.setFixedHeight(36)
        btn_load_company.clicked.connect(self._on_switch_company_profile)

        btn_new_company = SecondaryButton("MST mới")
        btn_new_company.setFixedHeight(36)
        btn_new_company.clicked.connect(self._on_prepare_new_company)

        self.lbl_company_slots = QLabel("Đã dùng 0/1 MST")
        self.lbl_company_slots.setStyleSheet(f"color: {COLORS['text_secondary']}; background: transparent;")

        profile_actions = QHBoxLayout()
        profile_actions.setContentsMargins(0, 0, 0, 0)
        profile_actions.setSpacing(8)
        profile_actions.addWidget(self.cbo_company_profiles, 1)
        profile_actions.addWidget(btn_new_company)
        profile_actions.addWidget(btn_load_company)

        profile_grid.addWidget(lbl_profiles, 0, 0, 1, 1)
        profile_grid.addLayout(profile_actions, 0, 1, 1, 1)
        profile_grid.addWidget(QLabel("Giới hạn gói:"), 1, 0, 1, 1)
        profile_grid.addWidget(self.lbl_company_slots, 1, 1, 1, 1)

        outer.addWidget(profile_box)

        box = QGroupBox("Thông tin Công ty / HKD")
        grid = QGridLayout(box)
        grid.setContentsMargins(20, 20, 20, 20)
        grid.setColumnStretch(1, 1)
        grid.setSpacing(12)

        # Removed self.inp_name as requested
        self.inp_legal_name = self._make_field_row(grid, 0, "Tên pháp lý",        "Tên đầy đủ theo ĐKKD")
        
        # Suffix button for MST
        btn_mst = QPushButton("🔍 Tra cứu")
        btn_mst.setFixedSize(80, 36)
        btn_mst.setObjectName("btn_secondary")
        btn_mst.clicked.connect(self._on_lookup_mst)
        
        self.inp_tax_id = self._make_field_row(grid, 1, "Mã số thuế",         "0123456789", suffix_btn=btn_mst)
        self.inp_address = self._make_field_row(grid, 2, "Địa chỉ",            "Số nhà, phường/xã, quận/huyện, tỉnh/thành")
        self.inp_email = self._make_field_row(grid, 3, "Email",               "ketoan@congty.com")
        self.inp_phone = self._make_field_row(grid, 4, "Số điện thoại",      "0912345678")
        self.inp_fax = self._make_field_row(grid, 5, "Fax",               "02412345678")
        self.inp_website = self._make_field_row(grid, 6, "Website",           "https://tenmiencuaban.vn")
        self.inp_rep = self._make_field_row(grid, 7, "Người đại diện",     "Họ và tên người đại diện pháp luật")
        self.inp_pos = self._make_field_row(grid, 8, "Chức vụ",            "Giám đốc / Chủ hộ kinh doanh")
        self.inp_bank = self._make_field_row(grid, 9, "Tài khoản ngân hàng", "Số tài khoản - Ngân hàng")
        
        outer.addWidget(box)

        btn_row = QHBoxLayout()
        # Combined button: Save and then Navigate to Register
        btn_save_reg = PrimaryButton("💾  Lưu và Đăng ký")
        btn_save_reg.setFixedHeight(40)
        btn_save_reg.clicked.connect(self._on_save_and_register)
        
        btn_row.addStretch()
        btn_row.addWidget(btn_save_reg)
        outer.addLayout(btn_row)
        outer.addStretch()

        return w

    def _on_save_and_register(self):
        """Lưu thông tin công ty/HKD, không tự chuyển tab."""
        self._on_save_company()

    def _normalize_tax_id(self, tax_id: str) -> str:
        return "".join(ch for ch in str(tax_id or "").strip() if ch.isdigit())

    def _clear_company_form(self):
        self.inp_legal_name.clear()
        self.inp_tax_id.clear()
        self.inp_address.clear()
        self.inp_email.clear()
        self.inp_phone.clear()
        self.inp_fax.clear()
        self.inp_website.clear()
        self.inp_rep.clear()
        self.inp_pos.clear()
        self.inp_bank.clear()
        self.inp_tax_id.setFocus()

    def _refresh_company_profile_selector(self, main=None, selected_tax_id: str = ""):
        if not hasattr(self, "cbo_company_profiles"):
            return

        main = main or self.window()
        profiles = []
        if hasattr(main, "company_profiles") and isinstance(main.company_profiles, list):
            profiles = [profile for profile in main.company_profiles if isinstance(profile, dict) and profile.get("tax_id")]

        limit = 1
        if hasattr(main, "get_company_profile_limit"):
            try:
                limit = main.get_company_profile_limit()
            except Exception:
                limit = 1

        current_tax_id = self._normalize_tax_id(selected_tax_id)
        if not current_tax_id and hasattr(main, "company_info") and isinstance(main.company_info, dict):
            current_tax_id = self._normalize_tax_id(main.company_info.get("tax_id", ""))

        self.cbo_company_profiles.blockSignals(True)
        self.cbo_company_profiles.clear()
        for profile in profiles:
            tax_id = self._normalize_tax_id(profile.get("tax_id", ""))
            legal_name = (profile.get("legal_name") or profile.get("name") or tax_id).strip()
            label = f"{tax_id} - {legal_name}"
            if profile.get("is_active"):
                label += " (đang dùng)"
            self.cbo_company_profiles.addItem(label, tax_id)

        if self.cbo_company_profiles.count() > 0:
            selected_index = 0
            for idx in range(self.cbo_company_profiles.count()):
                if self._normalize_tax_id(self.cbo_company_profiles.itemData(idx) or "") == current_tax_id:
                    selected_index = idx
                    break
            self.cbo_company_profiles.setCurrentIndex(selected_index)
        self.cbo_company_profiles.blockSignals(False)

        self.lbl_company_slots.setText(f"Đã dùng {len(profiles)}/{limit} MST")

    def _on_prepare_new_company(self):
        self._clear_company_form()
        show_toast(self.window(), "Bạn có thể nhập MST mới rồi bấm Lưu và Đăng ký để thêm vào danh sách.")

    def _on_switch_company_profile(self):
        main = self.window()
        selected_tax_id = self._normalize_tax_id(self.cbo_company_profiles.currentData() or "")
        if not selected_tax_id:
            show_toast(self.window(), "Chưa có MST nào để chuyển.", type="warning")
            return

        if hasattr(main, "activate_company_profile") and main.activate_company_profile(selected_tax_id):
            self.refresh(getattr(main, "current_db_path", ""))
            self.refresh_activation(main)
            show_toast(self.window(), f"Đã chuyển sang MST {selected_tax_id}.")
            return

        QMessageBox.warning(self, "Công ty", "Không thể chuyển sang MST đã chọn.")

    def _get_activation_tab_index(self) -> int:
        """Tìm index tab Bản quyền theo tiêu đề để tránh phụ thuộc index cứng."""
        if not hasattr(self, "tabs"):
            return -1
        for i in range(self.tabs.count()):
            if "Bản quyền" in self.tabs.tabText(i):
                return i
        return -1

    def _on_register_nav(self):
        """Chuyển sang tab bản quyền."""
        idx = self._get_activation_tab_index()
        if idx >= 0:
            self.tabs.setCurrentIndex(idx)

    def _on_lookup_mst(self):
        mst = self.inp_tax_id.text().strip()
        if not mst: 
            show_toast(self.window(), "Vui lòng nhập MST trước!", type="warning")
            return

        import requests
        try:
            show_toast(self.window(), f"Đang tra cứu MST: {mst}...")
            url = f"https://api.xinvoice.vn/gdt-api/tax-payer/{mst}"
            headers = {'Accept': 'application/json', 'client-id': '', 'api-key': ''}
            resp = requests.get(url, headers=headers, timeout=10)
            biz = resp.json()

            if resp.status_code == 200 and biz:
                name = biz.get("name")
                address = biz.get("address")
                if name:
                    # Filter out duplicate Xinvoice headers
                    name_lines = [line.strip() for line in name.split('\n') if line.strip() and not line.strip().startswith("Thông tin Công ty")]
                    clean_name = name_lines[-1] if name_lines else name.strip()
                    self.inp_legal_name.setText(clean_name)
                if address: self.inp_address.setText(address)
                show_toast(self.window(), f"✅ Đã tìm thấy: {name}")
            else:
                QMessageBox.information(self, "Thông báo", "Không tìm thấy thông tin MST này.")
        except Exception as e:
            QMessageBox.critical(self, "Lỗi", f"Không thể tra cứu: {e}")

    def _build_contract_config_tab(self) -> QWidget:
        """Cấu hình cho phân hệ Hợp đồng."""
        w = QWidget()
        outer = QVBoxLayout(w)
        outer.setContentsMargins(0, 15, 0, 0)
        outer.setSpacing(20)

        box = QGroupBox("Cấu hình đánh số Hợp đồng")
        grid = QGridLayout(box)
        grid.setContentsMargins(20, 20, 20, 20)
        grid.setColumnStretch(1, 1)
        grid.setSpacing(12)

        help_text = (
            "<b>Các thẻ hỗ trợ:</b><br/>"
            "• {YYYY}, {YY}: Năm (2026, 26)<br/>"
            "• {MM}, {DD}: Tháng, Ngày (03, 15)<br/>"
            "• {HH}, {mm}, {SS}: Giờ, Phút, Giây (14, 30, 05)<br/>"
            "• {RAND4}, {RAND2}: Ký tự ngẫu nhiên (A1B2, X9)<br/>"
            "• <i>Ví dụ: HD-{YYYY}{MM}-{HH}{mm}{SS}-{RAND4}</i>"
        )
        help_lbl = QLabel(help_text)
        help_lbl.setTextFormat(Qt.TextFormat.RichText)
        help_lbl.setStyleSheet("color: #64748b; background: #f8fafc; padding: 10px; border-radius: 6px;")

        btn_reset_inline = QPushButton("🔄 Mặc định")
        btn_reset_inline.setFixedSize(100, 36)
        btn_reset_inline.setObjectName("btn_secondary")
        btn_reset_inline.clicked.connect(self._on_reset_contract_format)

        self.inp_contract_format = self._make_field_row(
            grid, 0, "Mẫu Mã Hợp đồng",
            "HD-{YYYY}{MM}{DD}-{RAND4}",
            value="HD-{YYYY}{MM}{DD}-{RAND4}",
            suffix_btn=btn_reset_inline
        )

        grid.addWidget(help_lbl, 1, 0, 1, 2)

        outer.addWidget(box)

        # ── Email Config GroupBox ───────────────────────────
        email_box = QGroupBox("Cấu hình Email (IMAP/SMTP)")
        email_grid = QGridLayout(email_box)
        email_grid.setContentsMargins(20, 20, 20, 20)
        email_grid.setColumnStretch(1, 1)
        email_grid.setSpacing(12)

        self.inp_imap_server = self._make_field_row(email_grid, 0, "IMAP Server", "imap.gmail.com")
        self.inp_imap_port = self._make_field_row(email_grid, 1, "IMAP Port", "993")
        self.inp_smtp_server = self._make_field_row(email_grid, 2, "SMTP Server", "smtp.gmail.com")
        self.inp_smtp_port = self._make_field_row(email_grid, 3, "SMTP Port", "465 / 587")
        self.inp_mail_user = self._make_field_row(email_grid, 4, "Tài khoản Email", "your-email@gmail.com")
        self.inp_mail_pass = self._make_field_row(email_grid, 5, "Mật khẩu App", "xxxx xxxx xxxx xxxx")
        self.inp_mail_pass.setEchoMode(QLineEdit.EchoMode.Password)

        btn_test_mail = SecondaryButton("⚡ Kiểm tra kết nối")
        btn_test_mail.clicked.connect(self._on_test_mail_connection)
        email_grid.addWidget(btn_test_mail, 6, 1, 1, 1, Qt.AlignmentFlag.AlignLeft)

        outer.addWidget(email_box)

        # ── Nội dung Email mặc định ─────────────────────────
        mail_body_box = QGroupBox("Nội dung Email gửi kèm Hợp đồng ký")
        mail_body_layout = QVBoxLayout(mail_body_box)
        mail_body_layout.setContentsMargins(20, 16, 20, 16)
        mail_body_layout.setSpacing(8)

        mail_body_hint = QLabel(
            "Dùng <b>{partner}</b>, <b>{c_id}</b>, <b>{our_company}</b>, "
            "<b>{att_stem}</b> (tên file đính kèm), <b>{return_name}</b> (tên file gửi lại) làm biến động."
        )
        mail_body_hint.setTextFormat(Qt.TextFormat.RichText)
        mail_body_hint.setStyleSheet("color: #64748b; font-size: 11px;")

        self.inp_mail_body = QTextEdit()
        self.inp_mail_body.setMinimumHeight(340)
        from PySide6.QtWidgets import QSizePolicy
        self.inp_mail_body.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
        self.inp_mail_body.setPlaceholderText(
            "Kính gửi Quý đối tác {partner},\n\nCông ty chúng tôi ({our_company}) ..."
        )
        guide_block = build_contract_software_guide_block()
        default_body = (
            "Kính gửi Quý đối tác {partner},\n\n"
            "Công ty chúng tôi ({our_company}) xin gửi đến Quý đối tác "
            "hợp đồng số {c_id} đính kèm theo email này.\n\n"
            "Quý đối tác vui lòng xem xét, ký và gửi lại bản đã ký cho chúng tôi.\n\n"
            f"{guide_block}\n\n"
            "📎 Lưu ý khi gửi lại:\n"
            "  • Khi ký xong, vui lòng đặt tên file: {return_name}.pdf\n"
            "  • Gửi reply email này hoặc gửi về địa chỉ email của chúng tôi.\n\n"
            "Trân trọng,\n{our_company}"
        )
        self.inp_mail_body.setPlainText(default_body)

        mail_body_layout.addWidget(mail_body_hint)
        mail_body_layout.addWidget(self.inp_mail_body)
        outer.addWidget(mail_body_box)

        # ── Logo Chữ Ký Số ─────────────────────────────────
        logo_box = QGroupBox("Logo Chữ Ký Số (Xuất hiện trong khung chữ ký PDF)")
        logo_grid = QGridLayout(logo_box)
        logo_grid.setContentsMargins(20, 20, 20, 20)
        logo_grid.setColumnStretch(1, 1)
        logo_grid.setSpacing(12)

        lbl_logo = QLabel("File Logo:")
        lbl_logo.setFont(QFont("Segoe UI", 10))
        lbl_logo.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)

        self.inp_logo_path = QLineEdit()
        self.inp_logo_path.setPlaceholderText("Chưa chọn ảnh logo...")
        self.inp_logo_path.setReadOnly(True)
        self.inp_logo_path.setFixedHeight(36)

        btn_browse_logo = QPushButton("📂 Chọn ảnh")
        btn_browse_logo.setFixedSize(100, 36)
        btn_browse_logo.setObjectName("btn_secondary")
        btn_browse_logo.clicked.connect(self._on_browse_logo)

        btn_delete_logo = QPushButton("🗑️ Xóa")
        btn_delete_logo.setFixedSize(70, 36)
        btn_delete_logo.setObjectName("btn_danger") 
        btn_delete_logo.setToolTip("Xóa logo tùy chỉnh, quay về logo mặc định của phần mềm")
        btn_delete_logo.clicked.connect(self._on_delete_logo)

        self.lbl_logo_preview = QLabel("Đang dùng logo mặc định của phần mềm")
        self.lbl_logo_preview.setFixedSize(260, 88)
        self.lbl_logo_preview.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.lbl_logo_preview.setStyleSheet(
            "color: #94a3b8; font-style: italic; background: #f8fafc; border: 1px dashed #cbd5e1; border-radius: 10px;"
        )

        logo_hint = QLabel("Nếu chưa chọn logo riêng, phần mềm sẽ tự dùng logo mặc định khi ký hợp đồng.")
        logo_hint.setWordWrap(True)
        logo_hint.setStyleSheet(f"color: {COLORS['text_secondary']}; background: transparent;")

        logo_row = QHBoxLayout()
        logo_row.addWidget(self.inp_logo_path)
        logo_row.addWidget(btn_browse_logo)
        logo_row.addWidget(btn_delete_logo)

        logo_grid.addWidget(lbl_logo, 0, 0)
        logo_grid.addLayout(logo_row, 0, 1)
        logo_grid.addWidget(self.lbl_logo_preview, 1, 1)
        logo_grid.addWidget(logo_hint, 2, 1)

        outer.addWidget(logo_box)

        bg_box = QGroupBox("Hình Nền Chữ Ký Số")
        bg_grid = QGridLayout(bg_box)
        bg_grid.setContentsMargins(20, 20, 20, 20)
        bg_grid.setColumnStretch(1, 1)
        bg_grid.setSpacing(12)

        lbl_bg = QLabel("File nền:")
        lbl_bg.setFont(QFont("Segoe UI", 10))
        lbl_bg.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)

        self.inp_signature_bg_path = QLineEdit()
        self.inp_signature_bg_path.setPlaceholderText("Đang dùng nền mặc định hệ thống...")
        self.inp_signature_bg_path.setReadOnly(True)
        self.inp_signature_bg_path.setFixedHeight(36)

        btn_browse_bg = QPushButton("🖼️ Chọn nền")
        btn_browse_bg.setFixedSize(110, 36)
        btn_browse_bg.setObjectName("btn_secondary")
        btn_browse_bg.clicked.connect(self._on_browse_signature_bg)

        btn_delete_bg = QPushButton("🗑️ Xóa")
        btn_delete_bg.setFixedSize(70, 36)
        btn_delete_bg.setObjectName("btn_danger")
        btn_delete_bg.setToolTip("Xóa nền chữ ký tùy chỉnh, quay về nền mặc định hệ thống")
        btn_delete_bg.clicked.connect(self._on_delete_signature_bg)

        self.lbl_signature_bg_preview = QLabel("Đang dùng nền mặc định hệ thống")
        self.lbl_signature_bg_preview.setFixedSize(260, 96)
        self.lbl_signature_bg_preview.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.lbl_signature_bg_preview.setStyleSheet(
            "color: #94a3b8; font-style: italic; background: #f8fafc; border: 1px dashed #cbd5e1; border-radius: 10px;"
        )

        bg_hint = QLabel("Nền mặc định sẽ dùng màu brand của phần mềm và biểu tượng check để thể hiện file ký hợp lệ.")
        bg_hint.setWordWrap(True)
        bg_hint.setStyleSheet(f"color: {COLORS['text_secondary']}; background: transparent;")

        bg_row = QHBoxLayout()
        bg_row.addWidget(self.inp_signature_bg_path)
        bg_row.addWidget(btn_browse_bg)
        bg_row.addWidget(btn_delete_bg)

        bg_grid.addWidget(lbl_bg, 0, 0)
        bg_grid.addLayout(bg_row, 0, 1)
        bg_grid.addWidget(self.lbl_signature_bg_preview, 1, 1)
        bg_grid.addWidget(bg_hint, 2, 1)

        outer.addWidget(bg_box)

        display_box = QGroupBox("Tùy chọn hiển thị chữ ký số")
        display_layout = QHBoxLayout(display_box)
        display_layout.setContentsMargins(20, 16, 20, 16)
        display_layout.setSpacing(18)

        self.chk_signature_hide_logo = QCheckBox("Ẩn logo trong khung chữ ký số")
        self.chk_signature_hide_background = QCheckBox("Ẩn hình nền trong khung chữ ký số")
        self.chk_signature_hide_border = QCheckBox("Ẩn khung viền chữ ký số")
        self.chk_signature_hide_check = QCheckBox("Ẩn dấu check trên thông tin chữ ký số")
        for checkbox in (
            self.chk_signature_hide_logo,
            self.chk_signature_hide_background,
            self.chk_signature_hide_border,
            self.chk_signature_hide_check,
        ):
            checkbox.setStyleSheet("color: #334155; font-size: 12px;")
            display_layout.addWidget(checkbox)
        display_layout.addStretch()

        outer.addWidget(display_box)

        btn_row = QHBoxLayout()
        btn_save = QPushButton("💾  Lưu cấu hình")
        btn_save.setFixedHeight(40)
        btn_save.clicked.connect(self._on_save_company)

        btn_row.addStretch()
        btn_row.addWidget(btn_save)
        outer.addLayout(btn_row)
        outer.addStretch()

        return w

    def _get_signature_asset_path(self, filename: str) -> str:
        asset_path = get_app_data_path(os.path.join("sign", "assets", filename))
        os.makedirs(os.path.dirname(asset_path), exist_ok=True)
        return asset_path

    def _get_legacy_signature_asset_paths(self, filename: str) -> list[str]:
        src_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
        project_root = os.path.dirname(src_dir)
        return [
            os.path.join(project_root, "src", "sign", "assets", filename),
            os.path.join(project_root, "sign", "assets", filename),
        ]

    def _resolve_default_signature_logo_path(self) -> str:
        candidates = [
            get_resource_path(os.path.join("logo", "logofull.png")),
            get_resource_path(os.path.join("logo", "logo.png")),
            get_resource_path(os.path.join("gui", "assets", "logofull.png")),
            get_resource_path(os.path.join("gui", "assets", "app_icon.png")),
            get_resource_path(os.path.join("src", "gui", "assets", "logofull.png")),
            get_resource_path(os.path.join("src", "gui", "assets", "app_icon.png")),
        ]
        for path in candidates:
            if os.path.exists(path):
                return path
        return ""

    def _resolve_signature_logo_path(self) -> tuple[str, bool]:
        candidates = [self._get_signature_asset_path("logo.png")]
        for path in candidates:
            if os.path.exists(path):
                return path, True
        return self._resolve_default_signature_logo_path(), False

    def _resolve_signature_background_path(self) -> tuple[str, bool]:
        candidates = [self._get_signature_asset_path("signature_bg.png")]
        for path in candidates:
            if os.path.exists(path):
                return path, True
        return "", False

    def _apply_preview_pixmap(self, label: QLabel, pixmap: QPixmap, empty_text: str):
        if pixmap and not pixmap.isNull():
            scaled = pixmap.scaled(
                label.size(),
                Qt.AspectRatioMode.KeepAspectRatio,
                Qt.TransformationMode.SmoothTransformation,
            )
            label.setPixmap(scaled)
            label.setText("")
            return

        label.setPixmap(QPixmap())
        label.setText(empty_text)

    def _make_default_signature_bg_preview(self) -> QPixmap:
        width = max(self.lbl_signature_bg_preview.width(), 260)
        height = max(self.lbl_signature_bg_preview.height(), 96)
        pixmap = QPixmap(width, height)
        pixmap.fill(Qt.GlobalColor.transparent)

        painter = QPainter(pixmap)
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)

        path = QPainterPath()
        path.addRoundedRect(1, 1, width - 2, height - 2, 16, 16)

        gradient = QLinearGradient(0, 0, 0, height)
        gradient.setColorAt(0, QColor("#f8fafc"))
        gradient.setColorAt(1, QColor("#dbeafe"))
        painter.fillPath(path, gradient)

        painter.setPen(QPen(QColor("#93c5fd"), 1))
        painter.drawPath(path)

        inner_path = QPainterPath()
        inner_path.addRoundedRect(12, 12, width - 24, height - 24, 12, 12)
        painter.fillPath(inner_path, QColor(255, 255, 255, 205))

        painter.setPen(QPen(QColor("#1d4ed8"), 1.5))
        painter.drawRoundedRect(12, 12, width - 24, height - 24, 12, 12)

        painter.setBrush(QColor("#16a34a"))
        painter.setPen(Qt.PenStyle.NoPen)
        painter.drawEllipse(width - 54, 14, 30, 30)

        painter.setPen(QPen(QColor("white"), 3))
        painter.drawLine(width - 47, 30, width - 41, 36)
        painter.drawLine(width - 41, 36, width - 31, 22)

        painter.setPen(QColor("#1e40af"))
        painter.setFont(QFont("Segoe UI", 10, QFont.Weight.Bold))
        painter.drawText(20, 24, width - 80, 24, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, "Nền ký số mặc định")
        painter.setPen(QColor("#475569"))
        painter.setFont(QFont("Segoe UI", 8))
        painter.drawText(20, 46, width - 40, 24, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, "Theo màu brand của phần mềm")
        painter.drawText(20, 64, width - 40, 22, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, "Có dấu check xác nhận file ký hợp lệ")

        painter.end()
        return pixmap

    def _refresh_signature_asset_previews(self):
        if hasattr(self, "lbl_logo_preview"):
            logo_path, is_custom_logo = self._resolve_signature_logo_path()
            logo_pixmap = QPixmap(logo_path) if logo_path and os.path.exists(logo_path) else QPixmap()
            self._apply_preview_pixmap(self.lbl_logo_preview, logo_pixmap, "Đang dùng logo mặc định của phần mềm")
            self.inp_logo_path.setText(logo_path if is_custom_logo else "Mặc định: logo phần mềm")
            self.inp_logo_path.setToolTip(logo_path or "Đang dùng logo mặc định của phần mềm")

        if hasattr(self, "lbl_signature_bg_preview"):
            bg_path, is_custom_bg = self._resolve_signature_background_path()
            bg_pixmap = QPixmap(bg_path) if bg_path and os.path.exists(bg_path) else self._make_default_signature_bg_preview()
            self._apply_preview_pixmap(self.lbl_signature_bg_preview, bg_pixmap, "Đang dùng nền mặc định hệ thống")
            self.inp_signature_bg_path.setText(bg_path if is_custom_bg else "Mặc định hệ thống")
            self.inp_signature_bg_path.setToolTip(bg_path or "Đang dùng nền mặc định hệ thống")

    def _save_signature_asset(self, src_path: str, filename: str, success_message: str):
        import shutil

        try:
            dest = self._get_signature_asset_path(filename)
            if os.path.abspath(src_path) != os.path.abspath(dest):
                shutil.copy2(src_path, dest)
            show_toast(self.window(), success_message)
            return dest
        except Exception as e:
            QMessageBox.warning(self, "Cảnh báo", f"Không thể lưu file cấu hình chữ ký: {e}")
            return ""

    def _on_browse_logo(self):
        """Mở hộp thoại chọn ảnh logo cho chữ ký số và lưu vào thư mục cấu hình bền vững."""
        path, _ = QFileDialog.getOpenFileName(
            self, "Chọn ảnh Logo Chữ Ký", "",
            "Ảnh (*.png *.jpg *.jpeg *.bmp *.gif)"
        )
        if not path:
            return
        if self._save_signature_asset(path, "logo.png", "✅ Đã lưu logo chữ ký tùy chỉnh."):
            self._refresh_signature_asset_previews()

    def _on_browse_signature_bg(self):
        path, _ = QFileDialog.getOpenFileName(
            self,
            "Chọn hình nền chữ ký số",
            "",
            "Ảnh (*.png *.jpg *.jpeg *.bmp *.gif)",
        )
        if not path:
            return
        if self._save_signature_asset(path, "signature_bg.png", "✅ Đã lưu hình nền chữ ký số."):
            self._refresh_signature_asset_previews()

    def _on_delete_logo(self):
        """Xóa logo tùy chỉnh và quay về logo mặc định của phần mềm."""
        try:
            deleted_any = False
            logo_candidates = [self._get_signature_asset_path("logo.png"), *self._get_legacy_signature_asset_paths("logo.png")]
            for path in logo_candidates:
                if os.path.exists(path):
                    os.remove(path)
                    deleted_any = True

            self._refresh_signature_asset_previews()
            if deleted_any:
                show_toast(self.window(), "✅ Đã xóa logo tùy chỉnh. Đang dùng logo mặc định của phần mềm.")
            else:
                show_toast(self.window(), "ℹ️ Hiện chưa có logo tùy chỉnh để xóa")
        except Exception as e:
            QMessageBox.warning(self, "Cảnh báo", f"Không thể xóa logo: {e}")

    def _on_delete_signature_bg(self):
        try:
            deleted_any = False
            bg_candidates = [
                self._get_signature_asset_path("signature_bg.png"),
                *self._get_legacy_signature_asset_paths("signature_bg.png"),
            ]
            for path in bg_candidates:
                if os.path.exists(path):
                    os.remove(path)
                    deleted_any = True

            self._refresh_signature_asset_previews()
            if deleted_any:
                show_toast(self.window(), "✅ Đã xóa nền chữ ký tùy chỉnh. Đang dùng nền mặc định hệ thống.")
            else:
                show_toast(self.window(), "ℹ️ Hiện chưa có nền chữ ký tùy chỉnh để xóa")
        except Exception as e:
            QMessageBox.warning(self, "Cảnh báo", f"Không thể xóa nền chữ ký: {e}")

    def _on_reset_contract_format(self):
        """Khôi phục định dạng mã hợp đồng về mặc định."""
        default_fmt = "HD-{YYYY}{MM}{DD}-{RAND4}"
        self.inp_contract_format.setText(default_fmt)
        show_toast(self.window(), "Đã đưa về định dạng mặc định. Nhấn 'Lưu' để áp dụng.")


    def _on_test_mail_connection(self):
        """Kiểm tra kết nối IMAP/SMTP."""
        import imaplib
        import smtplib
        import ssl

        user = self.inp_mail_user.text().strip()
        password = self.inp_mail_pass.text().strip()
        imap_server = self.inp_imap_server.text().strip()
        imap_port_text = self.inp_imap_port.text().strip()
        smtp_server = self.inp_smtp_server.text().strip()
        smtp_port_text = self.inp_smtp_port.text().strip()

        if not all([user, password, imap_server, imap_port_text, smtp_server, smtp_port_text]):
            show_toast(self.window(), "Vui lòng nhập đầy đủ cấu hình IMAP/SMTP.", type="error")
            return

        try:
            imap_port = int(imap_port_text)
            smtp_port = int(smtp_port_text)
        except ValueError:
            show_toast(self.window(), "Port IMAP/SMTP phải là số.", type="error")
            return

        show_toast(self.window(), "Đang kiểm tra kết nối Email...")

        # IMAP check
        try:
            imap = imaplib.IMAP4_SSL(imap_server, imap_port, timeout=10)
            imap.login(user, password)
            imap.logout()
        except Exception as e:
            QMessageBox.warning(self, "Lỗi IMAP", f"Kết nối IMAP thất bại:\n{e}")
            show_toast(self.window(), "❌ Kiểm tra kết nối thất bại (IMAP).", type="error")
            return

        # SMTP check
        try:
            ctx = ssl.create_default_context()
            if smtp_port == 465:
                with smtplib.SMTP_SSL(smtp_server, smtp_port, timeout=10, context=ctx) as server:
                    server.login(user, password)
            else:
                with smtplib.SMTP(smtp_server, smtp_port, timeout=10) as server:
                    server.ehlo()
                    server.starttls(context=ctx)
                    server.ehlo()
                    server.login(user, password)
        except Exception as e:
            QMessageBox.warning(self, "Lỗi SMTP", f"Kết nối SMTP thất bại:\n{e}")
            show_toast(self.window(), "❌ Kiểm tra kết nối thất bại (SMTP).", type="error")
            return

        show_toast(self.window(), "✅ Kết nối IMAP/SMTP thành công.")

    def _build_system_tab(self) -> QWidget:
        w = QWidget()
        outer = QVBoxLayout(w)
        outer.setContentsMargins(0, 15, 0, 0)
        outer.setSpacing(20)

        version_box = QGroupBox("Thông tin phần mềm")
        version_layout = QVBoxLayout(version_box)
        version_layout.setContentsMargins(20, 16, 20, 16)
        version_layout.setSpacing(6)

        current_version = QApplication.instance().applicationVersion() if QApplication.instance() else "1.0.5"

        lbl_version = QLabel(f"Phiên bản hiện tại: {current_version}")
        lbl_version.setFont(QFont("Segoe UI", 11, QFont.Weight.Bold))

        lbl_hint = QLabel("Bạn có thể xem nhanh phiên bản ở thanh trạng thái phía dưới cửa sổ hoặc tại mục này.")
        lbl_hint.setWordWrap(True)
        lbl_hint.setStyleSheet("color: #475569;")

        version_layout.addWidget(lbl_version)
        version_layout.addWidget(lbl_hint)

        outer.addWidget(version_box)


        # ── Backup Buttons ───────────────────────────
        btn_layout = QHBoxLayout()

        self.btn_backup_local = QPushButton("💾  Backup Cục bộ (Tạo file .zip)")
        self.btn_backup_local.setFixedHeight(40)
        self.btn_backup_local.setObjectName("btn_secondary")
        self.btn_backup_local.clicked.connect(self._on_backup_local)

        self.btn_restore_local = QPushButton("📂  Restore từ máy tính")
        self.btn_restore_local.setFixedHeight(40)
        self.btn_restore_local.setObjectName("btn_secondary")
        self.btn_restore_local.clicked.connect(self._on_restore_local)

        self.btn_backup_drive = QPushButton("☁️  Backup lên Google Drive")
        self.btn_backup_drive.setFixedHeight(40)
        self.btn_backup_drive.setObjectName("btn_success")
        self.btn_backup_drive.clicked.connect(self._on_backup_drive)

        self.btn_restore_drive = QPushButton("📥  Restore từ Google Drive")
        self.btn_restore_drive.setFixedHeight(40)
        self.btn_restore_drive.setObjectName("btn_secondary")
        self.btn_restore_drive.clicked.connect(self._on_restore_drive)

        btn_layout.addWidget(self.btn_backup_local)
        btn_layout.addWidget(self.btn_restore_local)
        btn_layout.addWidget(self.btn_backup_drive)
        btn_layout.addWidget(self.btn_restore_drive)
        btn_layout.addStretch()

        outer.addLayout(btn_layout)
        outer.addStretch()

        return w



    def _build_tax_tab(self) -> QWidget:
        w = QWidget()
        outer = QVBoxLayout(w)
        outer.setContentsMargins(0, 15, 0, 0)
        outer.setSpacing(15)

        box = QGroupBox("Cấu hình thuế áp dụng (2026)")
        fl = QVBoxLayout(box)
        fl.setContentsMargins(20, 15, 20, 15)
        fl.setSpacing(10)

        tax_configs = [
            ("Ngưỡng miễn thuế HKD:",        "500.000.000 đ/năm  (Luật TNCN 109/2025/QH15)"),
            ("Thuế suất TNDN tiêu chuẩn:",   "20%  (DN thông thường)"),
            ("Thuế suất TNDN ưu đãi SME:",   "17%  (DN vừa và nhỏ đủ điều kiện)"),
            ("Chuẩn mực kế toán DN vừa/lớn:","Thông tư 200/2014/TT-BTC (VAS)"),
            ("Chuẩn mực DN nhỏ/siêu nhỏ:",  "Thông tư 133/2016/TT-BTC"),
            ("Chuẩn mực Hộ kinh doanh:",     "Thông tư 152/2025/TT-BTC (từ 01/01/2026)"),
        ]

        for label, val in tax_configs:
            row = QHBoxLayout()
            lbl = QLabel(label)
            lbl.setObjectName("label_muted")
            lbl.setFixedWidth(250)
            val_lbl = QLabel(val)
            val_lbl.setFont(QFont("Segoe UI", 9))
            val_lbl.setStyleSheet(f"color: {COLORS['accent_light']}; background: transparent;")
            row.addWidget(lbl)
            row.addWidget(val_lbl)
            row.addStretch()
            fl.addLayout(row)

        outer.addWidget(box)
        outer.addStretch()
        return w

    def _build_activation_tab(self) -> QWidget:
        """Tab Bản quyền - Hiển thị trạng thái và thông tin kích hoạt theo cấu trúc rõ ràng."""
        w = QWidget()
        outer = QVBoxLayout(w)
        outer.setContentsMargins(12, 16, 12, 8)
        outer.setSpacing(14)

        banner = QFrame()
        banner.setStyleSheet(
            "QFrame {"
            "background: #f8fafc;"
            "border: 1px solid #dbeafe;"
            "border-radius: 12px;"
            "padding: 12px;"
            "}"
        )
        banner_layout = QVBoxLayout(banner)
        banner_layout.setContentsMargins(14, 12, 14, 12)
        banner_layout.setSpacing(6)

        self.lbl_license_status_big = QLabel("Đang kiểm tra bản quyền...")
        self.lbl_license_status_big.setObjectName("page_title")
        self.lbl_license_status_big.setWordWrap(True)
        self.lbl_license_status_big.setAlignment(Qt.AlignmentFlag.AlignLeft)
        self.lbl_license_status_big.setStyleSheet("color: #0f172a; font-size: 18px; font-weight: 700;")

        self.lbl_license_details = QLabel("")
        self.lbl_license_details.setWordWrap(True)
        self.lbl_license_details.setAlignment(Qt.AlignmentFlag.AlignLeft)
        self.lbl_license_details.setStyleSheet("color: #475569; font-size: 13px;")

        self.lbl_cloud_noti = QLabel("")
        self.lbl_cloud_noti.setWordWrap(True)
        self.lbl_cloud_noti.setAlignment(Qt.AlignmentFlag.AlignLeft)
        self.lbl_cloud_noti.setStyleSheet(
            "color: #9a3412; background: #fff7ed; border: 1px solid #fed7aa; "
            "border-radius: 8px; padding: 8px 10px;"
        )
        self.lbl_cloud_noti.hide()

        banner_layout.addWidget(self.lbl_license_status_big)
        banner_layout.addWidget(self.lbl_license_details)
        banner_layout.addWidget(self.lbl_cloud_noti)
        outer.addWidget(banner)

        details_box = QGroupBox("Chi tiết kích hoạt")
        details_grid = QGridLayout(details_box)
        details_grid.setContentsMargins(14, 12, 14, 12)
        details_grid.setHorizontalSpacing(12)
        details_grid.setVerticalSpacing(10)
        details_grid.setColumnStretch(1, 1)

        lbl_plan = QLabel("Gói hiện tại:")
        lbl_plan.setObjectName("label_muted")
        self.lbl_license_plan = QLabel("-")
        self.lbl_license_plan.setStyleSheet("font-weight: 700; color: #0f172a;")

        lbl_expiry = QLabel("Hạn sử dụng:")
        lbl_expiry.setObjectName("label_muted")
        self.lbl_license_expiry = QLabel("-")
        self.lbl_license_expiry.setStyleSheet("color: #334155;")

        lbl_id = QLabel("Định danh:")
        lbl_id.setObjectName("label_muted")
        self.lbl_license_identity = QLabel("-")
        self.lbl_license_identity.setWordWrap(True)
        self.lbl_license_identity.setStyleSheet("color: #334155;")

        lbl_limits = QLabel("Giới hạn gói:")
        lbl_limits.setObjectName("label_muted")
        self.lbl_license_limits = QLabel("-")
        self.lbl_license_limits.setWordWrap(True)
        self.lbl_license_limits.setStyleSheet("color: #334155;")

        details_grid.addWidget(lbl_plan, 0, 0)
        details_grid.addWidget(self.lbl_license_plan, 0, 1)
        details_grid.addWidget(lbl_expiry, 1, 0)
        details_grid.addWidget(self.lbl_license_expiry, 1, 1)
        details_grid.addWidget(lbl_id, 2, 0)
        details_grid.addWidget(self.lbl_license_identity, 2, 1)
        details_grid.addWidget(lbl_limits, 3, 0)
        details_grid.addWidget(self.lbl_license_limits, 3, 1)

        key_box = QGroupBox("Nhập key kích hoạt")
        key_grid = QGridLayout(key_box)
        key_grid.setContentsMargins(14, 12, 14, 12)
        key_grid.setHorizontalSpacing(10)
        key_grid.setVerticalSpacing(8)
        key_grid.setColumnStretch(1, 1)

        key_label = QLabel("License key:")
        key_label.setObjectName("label_muted")
        self.inp_license_key = QLineEdit()
        self.inp_license_key.setPlaceholderText("VD: LIC-ABCD-1234-EFGH-5678")
        self.inp_license_key.setClearButtonEnabled(True)

        btn_activate_key = PrimaryButton("Kích hoạt key")
        btn_activate_key.clicked.connect(self._on_activate_license_key)

        key_hint = QLabel(
            "Nhập key được cấp trên server. Phần mềm sẽ kiểm tra key, đồng bộ gói, hạn dùng và giới hạn MST/thiết bị."
        )
        key_hint.setWordWrap(True)
        key_hint.setStyleSheet("color: #64748b;")

        key_grid.addWidget(key_label, 0, 0)
        key_grid.addWidget(self.inp_license_key, 0, 1)
        key_grid.addWidget(btn_activate_key, 0, 2)
        key_grid.addWidget(key_hint, 1, 0, 1, 3)
        outer.addWidget(key_box)
        outer.addWidget(details_box)

        action_row = QHBoxLayout()
        action_row.setSpacing(8)
        btn_refresh_license = SecondaryButton("🔄 Kiểm tra lại bản quyền")
        btn_refresh_license.clicked.connect(self._on_refresh_license)
        action_row.addStretch()
        action_row.addWidget(btn_refresh_license)
        outer.addLayout(action_row)

        help_box = QLabel(
            "<b>Gợi ý:</b> Nếu trạng thái chưa đúng, hãy bấm <b>Kiểm tra lại bản quyền</b> "
            "sau khi đã lưu đầy đủ MST trong tab Thông tin Công ty."
        )
        help_box.setWordWrap(True)
        help_box.setTextFormat(Qt.TextFormat.RichText)
        help_box.setStyleSheet("color: #64748b; background: #f8fafc; border-radius: 8px; padding: 10px;")
        outer.addWidget(help_box)

        outer.addStretch()
        
        return w

    def _is_info_complete(self):
        """Kiểm tra toàn bộ 7 trường thông tin bắt buộc."""
        fields = [
            self.inp_legal_name.text().strip(),
            self.inp_tax_id.text().strip(),
            self.inp_address.text().strip(),
            self.inp_email.text().strip(),
            self.inp_phone.text().strip(),
            self.inp_rep.text().strip(),
            self.inp_pos.text().strip()
        ]
        return all(fields)

    def _on_activate_license_key(self):
        """Lưu và kích hoạt license key thủ công từ tab Bản quyền."""
        key = self.inp_license_key.text().strip() if hasattr(self, "inp_license_key") else ""
        if not key:
            QMessageBox.warning(self, "Bản quyền", "Vui lòng nhập license key trước khi kích hoạt.")
            return

        main = self.window()
        if not main:
            return

        tax_id = ""
        if hasattr(main, "company_info") and isinstance(main.company_info, dict):
            tax_id = (main.company_info.get("tax_id") or "").strip()

        try:
            if hasattr(main, "save_license"):
                main.save_license(key)
            else:
                setattr(main, "license_key", key)
                if hasattr(main, "check_license"):
                    main.check_license()

            try:
                from core.licensing import ensure_device_registered
                reg = ensure_device_registered(tax_id=tax_id, license_key=key)
                if not reg.get("ok"):
                    print(f"[license] Key activation device sync warning: {reg}")
            except Exception as exc:
                print(f"[license] Key activation sync warning: {exc}")

            self.refresh_activation(main)
            if getattr(main, "is_license_blocked", False):
                QMessageBox.warning(self, "Bản quyền", getattr(main, "license_block_reason", "") or "Key đã bị khóa trên server.")
            elif getattr(main, "is_cloud_active", False):
                show_toast(self.window(), "Đã kích hoạt license key thành công.")
                QMessageBox.information(self, "Bản quyền", "Đã kích hoạt license key thành công.")
            else:
                QMessageBox.warning(self, "Bản quyền", "Chưa kích hoạt được key. Vui lòng kiểm tra key, MST hoặc trạng thái trên server.")
        except Exception as e:
            QMessageBox.warning(self, "Bản quyền", f"Không thể kích hoạt key:\n{e}")


    def _on_refresh_license(self):
        """Làm mới trạng thái bản quyền từ nguồn cloud/local."""
        main = self.window()
        if not main:
            return
        try:
            if hasattr(main, "check_license"):
                main.check_license()
            self.refresh_activation(main)
            show_toast(self.window(), "Đã cập nhật trạng thái bản quyền")
        except Exception as e:
            QMessageBox.warning(self, "Bản quyền", f"Không thể cập nhật trạng thái bản quyền:\n{e}")


    def _on_backup_local(self):
        """Thực thi backup DB ra file zip vào thư mục do người dùng chọn."""
        from core.backup import backup_all
        from core.utils import get_company_root_path
        import os
        try:
            output_dir = QFileDialog.getExistingDirectory(
                self,
                "Chọn thư mục lưu file backup",
                get_company_root_path(os.path.join("database", "backups")),
            )
            if not output_dir:
                return

            # Lấy db path từ main window
            main_win = self.window()
            db_path = getattr(main_win, 'current_db_path', '')
            results = backup_all(
                note="Manual Backup from Settings",
                db_path=db_path or None,
                output_dir=output_dir,
            )
            if results:
                zip_path = results[0]
                show_toast(self.window(), f"Backup thành công: {os.path.basename(zip_path)}")
                QMessageBox.information(self, "Backup thành công", f"Đã lưu file backup tại:\n{zip_path}")
            else:
                QMessageBox.warning(self, "Cảnh báo", "Không có CSDL nào để backup hoặc có lỗi xảy ra.")
        except Exception as e:
            QMessageBox.critical(self, "❌ Lỗi", f"Lỗi khi backup cục bộ:\n{e}")

    def _on_restore_local(self):
        """Khôi phục dữ liệu từ file backup .zip do người dùng chọn trên máy tính."""
        from core.backup import restore_backup
        from core.utils import get_company_root_path
        import os

        zip_path, _ = QFileDialog.getOpenFileName(
            self,
            "Chọn file backup để khôi phục",
            get_company_root_path(os.path.join("database", "backups")),
            "Backup ZIP (*.zip)",
        )
        if not zip_path:
            return

        confirm = QMessageBox.question(
            self,
            "Xác nhận khôi phục",
            f"Bạn có chắc muốn khôi phục dữ liệu từ file:\n{os.path.basename(zip_path)}\n\n"
            "Phần mềm sẽ tự backup dữ liệu hiện tại trước khi restore.",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
            QMessageBox.StandardButton.No,
        )
        if confirm != QMessageBox.StandardButton.Yes:
            return

        try:
            if not restore_backup("", zip_path, confirm=True):
                QMessageBox.warning(self, "Khôi phục dữ liệu", "Restore thất bại. Dữ liệu hiện tại vẫn được giữ nguyên.")
                return

            self._refresh_after_restore()
            show_toast(self.window(), "Đã khôi phục dữ liệu từ file backup thành công!")
            QMessageBox.information(
                self,
                "Khôi phục thành công",
                "Dữ liệu đã được khôi phục từ file backup trên máy tính. Nếu màn hình chưa cập nhật hết, bạn hãy mở lại phần mềm một lần.",
            )
        except Exception as e:
            QMessageBox.critical(self, "❌ Lỗi", f"Không thể khôi phục dữ liệu từ file backup:\n{e}")


    def _ensure_backup_sync_allowed(self) -> bool:
        """Chặn Google Drive sync cho gói không có quyền theo BANG_GIA."""
        from core.licensing import format_plan_label, get_effective_plan_limits

        main_win = self.window()
        plan_id = getattr(main_win, "plan_type", "free") if main_win else "free"
        license_context = getattr(main_win, "license_context", {}) if main_win else {}
        limits = getattr(main_win, "effective_plan_limits", {}) or get_effective_plan_limits(plan_id, license_context)
        if limits.get("has_backup_sync"):
            return True

        QMessageBox.warning(
            self,
            "Google Drive Sync",
            (
                f"Gói hiện tại ({format_plan_label(plan_id)}) chưa bao gồm đồng bộ Google Drive.\n"
                "Vui lòng dùng TRIAL, STARTER, PRO hoặc OFFICE để sử dụng tính năng này."
            ),
        )
        return False

    def _on_backup_drive(self):
        """Thực thi backup và đẩy lên Google Drive cá nhân của user."""
        if not self._ensure_backup_sync_allowed():
            return

        from core.backup import backup_all
        from core.drive_sync import backup_file_to_drive
        
        # Disable button to prevent double clicks
        self.btn_backup_drive.setEnabled(False)
        self.btn_backup_drive.setText(" ⏳ Đang đẩy lên Drive... ")
        QApplication.processEvents()

        try:
            # 1. First, create a fresh local backup zip
            results = backup_all(note="Auto-backup for Google Drive Sync")
            if not results:
                QMessageBox.warning(self, "Cảnh báo", "Không thể tạo file backup cục bộ. Đã hủy đẩy lên Drive.")
                return
                
            # Assume we upload the most recently created zip (the first active company's backup)
            # In a real scenario with multiple companies, you might want to zip them all together
            zip_file = results[0] 
            
            # 2. Upload to Drive
            res = backup_file_to_drive(zip_file)
            
            if res.get("success"):
                show_toast(self.window(), "Đã tải backup lên Google Drive thành công!")
            else:
                QMessageBox.warning(self, "❌ Lỗi Upload", 
                    f"Không thể đẩy file lên Google Drive.\nChi tiết lỗi: {res.get('message')}"
                )
        except Exception as e:
             QMessageBox.critical(self, "❌ Lỗi", f"Đã xảy ra lỗi ngoài ý muốn:\n{e}")
        finally:
             self.btn_backup_drive.setEnabled(True)
             self.btn_backup_drive.setText("☁️  Backup lên Google Drive")

    def _format_drive_backup_label(self, file_info: dict) -> str:
        file_name = str(file_info.get("name", "Không tên")).strip() or "Không tên"
        modified_time = str(file_info.get("modifiedTime", "")).replace("T", " ").replace("Z", "")[:19]
        size_bytes = int(file_info.get("size") or 0)
        size_mb = size_bytes / 1_048_576 if size_bytes else 0
        modified_label = modified_time or "Không rõ ngày"
        size_label = f"{size_mb:.2f} MB" if size_mb else "N/A"
        return f"{file_name} | {modified_label} | {size_label}"

    def _refresh_after_restore(self):
        main_win = self.window()
        if not main_win:
            return

        db_path = getattr(main_win, "current_db_path", "")
        if db_path and hasattr(main_win, "set_db_path"):
            main_win.set_db_path(db_path)
        elif hasattr(main_win, "refresh_all_pages"):
            main_win.refresh_all_pages()

        if hasattr(main_win, "check_license"):
            try:
                main_win.check_license()
            except Exception:
                pass

    def _on_restore_drive(self):
        """Tải một bản backup .zip từ Google Drive và khôi phục dữ liệu hiện tại."""
        if not self._ensure_backup_sync_allowed():
            return

        from core.backup import restore_backup
        from core.drive_sync import download_backup_from_drive, list_drive_backup_files

        self.btn_restore_drive.setEnabled(False)
        self.btn_restore_drive.setText(" ⏳ Đang đọc Google Drive... ")
        QApplication.processEvents()

        try:
            result = list_drive_backup_files()
            if not result.get("success"):
                QMessageBox.warning(self, "Google Drive", result.get("message", "Không lấy được danh sách backup."))
                return

            files = result.get("files", []) or []
            if not files:
                QMessageBox.information(self, "Google Drive", "Chưa có file backup .zip nào trên Google Drive của MST hiện tại.")
                return

            label_map: dict[str, dict] = {}
            items: list[str] = []
            for file_info in files:
                label = self._format_drive_backup_label(file_info)
                label_map[label] = file_info
                items.append(label)

            selected_label, ok = QInputDialog.getItem(
                self,
                "Restore từ Google Drive",
                "Chọn bản backup cần khôi phục:",
                items,
                0,
                False,
            )
            if not ok or not selected_label:
                return

            selected_file = label_map[selected_label]
            file_name = selected_file.get("name", "")
            confirm = QMessageBox.question(
                self,
                "Xác nhận khôi phục",
                f"Bạn có chắc muốn khôi phục dữ liệu từ file:\n{file_name}\n\n"
                "Phần mềm sẽ tự backup dữ liệu hiện tại trước khi restore.",
                QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
                QMessageBox.StandardButton.No,
            )
            if confirm != QMessageBox.StandardButton.Yes:
                return

            self.btn_restore_drive.setText(" ⏳ Đang tải backup từ Drive... ")
            QApplication.processEvents()

            download_result = download_backup_from_drive(selected_file.get("id", ""), file_name)
            if not download_result.get("success"):
                QMessageBox.warning(self, "Google Drive", download_result.get("message", "Không tải được backup từ Google Drive."))
                return

            self.btn_restore_drive.setText(" ⏳ Đang restore dữ liệu... ")
            QApplication.processEvents()

            if not restore_backup("", download_result.get("path", ""), confirm=True):
                QMessageBox.warning(self, "Khôi phục dữ liệu", "Restore thất bại. Dữ liệu hiện tại vẫn được giữ nguyên.")
                return

            self._refresh_after_restore()
            show_toast(self.window(), "Đã khôi phục dữ liệu từ Google Drive thành công!")
            QMessageBox.information(
                self,
                "Khôi phục thành công",
                "Dữ liệu đã được khôi phục từ Google Drive. Nếu màn hình chưa cập nhật hết, bạn hãy mở lại phần mềm một lần.",
            )
        except Exception as e:
            QMessageBox.critical(self, "❌ Lỗi", f"Không thể khôi phục dữ liệu từ Google Drive:\n{e}")
        finally:
            self.btn_restore_drive.setEnabled(True)
            self.btn_restore_drive.setText("📥  Restore từ Google Drive")

    def _on_save_company(self):
        """Lưu thông tin công ty vào MainWindow và xuất ra info.json."""
        import json
        
        legal_name = self.inp_legal_name.text().strip()
        tax_id = self.inp_tax_id.text().strip()
        address = self.inp_address.text().strip()
        email = self.inp_email.text().strip()
        phone = self.inp_phone.text().strip()
        fax = self.inp_fax.text().strip()
        website = self.inp_website.text().strip()
        representative = self.inp_rep.text().strip()
        position = self.inp_pos.text().strip()
        
        # Validation
        if not all([legal_name, tax_id, address, email, phone, representative, position]):
            show_toast(self.window(), "Thông tin chưa đầy đủ! Vui lòng nhập tất cả các ô để lưu và đăng ký.", type="warning")
            return False
        info_data = {
            "legal_name":     legal_name,
            "tax_id":         self._normalize_tax_id(tax_id) or tax_id,
            "address":        address,
            "email":          email,
            "phone":          phone,
            "fax":            fax,
            "website":        website,
            "representative": representative,
            "position":       position,
            "bank_account":   self.inp_bank.text().strip(),
            "contract_format": self.inp_contract_format.text().strip(),
            "mail_body": self.inp_mail_body.toPlainText().strip() if hasattr(self, 'inp_mail_body') else "",
            "signature_options": {
                "hide_logo": bool(getattr(self, "chk_signature_hide_logo", None) and self.chk_signature_hide_logo.isChecked()),
                "hide_background": bool(getattr(self, "chk_signature_hide_background", None) and self.chk_signature_hide_background.isChecked()),
                "hide_border": bool(getattr(self, "chk_signature_hide_border", None) and self.chk_signature_hide_border.isChecked()),
                "hide_check": bool(getattr(self, "chk_signature_hide_check", None) and self.chk_signature_hide_check.isChecked()),
            },
            "mail_config": {
                "imap_server": self.inp_imap_server.text().strip(),
                "imap_port":   self.inp_imap_port.text().strip(),
                "smtp_server": self.inp_smtp_server.text().strip(),
                "smtp_port":   self.inp_smtp_port.text().strip(),
                "user":        self.inp_mail_user.text().strip(),
                "pass":        self.inp_mail_pass.text().strip(),
            }
        }
        
        main = self.window()
        normalized_tax_id = info_data["tax_id"]
        is_new_profile = False
        new_db_path = ""

        if hasattr(main, "company_profiles") and hasattr(main, "get_company_profile_limit"):
            existing_tax_ids = {
                self._normalize_tax_id(profile.get("tax_id", ""))
                for profile in getattr(main, "company_profiles", [])
                if isinstance(profile, dict) and profile.get("tax_id")
            }
            if normalized_tax_id not in existing_tax_ids and len(existing_tax_ids) >= main.get_company_profile_limit():
                QMessageBox.warning(
                    self,
                    "Đã đạt giới hạn MST",
                    f"Gói hiện tại chỉ cho phép tối đa {main.get_company_profile_limit()} MST.\n"
                    "Bạn cần nâng gói hoặc xóa bớt một MST đã lưu trước khi thêm MST mới.",
                )
                return False

        if hasattr(main, "upsert_company_profile"):
            ok, is_new_profile, new_db_path = main.upsert_company_profile(info_data, activate=True)
            if not ok:
                QMessageBox.warning(self, "Công ty", "Không thể lưu MST hiện tại.")
                return False
        elif hasattr(main, "company_info"):
            main.company_info.update(info_data)
            if hasattr(main, "save_settings"):
                main.save_settings()

        if hasattr(main, "company_info"):
            try:
                from core.auth import sync_account_company_info
                sync_account_company_info(info_data)
            except Exception as e:
                print(f"[auth] Lỗi cập nhật hồ sơ tài khoản: {e}")

            if hasattr(main, "set_active_db"):
                if not new_db_path:
                    from core.company_manager import get_company_db_path_for_tax_id
                    new_db_path = get_company_db_path_for_tax_id(normalized_tax_id)
                if not os.path.exists(os.path.dirname(new_db_path)):
                    os.makedirs(os.path.dirname(new_db_path), exist_ok=True)
                main.set_active_db(new_db_path)

            if hasattr(main, "check_license"):
                main.check_license(info_data)
                try:
                    from core.licensing import ensure_device_registered
                    reg = ensure_device_registered(tax_id=normalized_tax_id, license_key=getattr(main, "license_key", ""))
                    if reg.get("ok"):
                        show_toast(
                            self.window(),
                            f"Đã đồng bộ thiết bị ({reg.get('app_id')}) · MID: {str(reg.get('machine_id', ''))[:12]}..."
                        )
                    else:
                        print(f"[license] Device sync chưa thành công: {reg}")
                except Exception as e:
                    print(f"[license] Lỗi đồng bộ thiết bị: {e}")
                # Cập nhật ngay lập tức các label trong tab Bản quyền
                self.refresh_activation(main)

        self._refresh_company_profile_selector(main, normalized_tax_id)
        used_count = len([profile for profile in getattr(main, "company_profiles", []) if isinstance(profile, dict) and profile.get("tax_id")])
        if is_new_profile:
            show_toast(self.window(), f"Đã thêm MST {normalized_tax_id} vào danh sách quản lý ({used_count}/{getattr(main, 'get_company_profile_limit', lambda: 1)()} MST).")
        else:
            show_toast(self.window(), "Đã lưu thông tin và cấu hình hệ thống!")
        return True

    def refresh(self, db_path: str):
        """Update settings display with active database."""
        main = self.window()
        info = {}
        if hasattr(main, "company_info") and main.company_info:
            info.update(main.company_info)
            
        # New: Cloud-sync fallback if local info is missing
        if hasattr(main, "cloud_company_info") and main.cloud_company_info:
            c_info = main.cloud_company_info
            # Only fill if local is empty/none
            if not info.get("legal_name"): info["legal_name"] = c_info.get("legal_name", "")
            if not info.get("tax_id"):    info["tax_id"]    = c_info.get("tax_id", "")
            if not info.get("address"):   info["address"]   = c_info.get("address", "")
            if not info.get("email"):     info["email"]     = c_info.get("email", "")
            if not info.get("phone"):     info["phone"]     = c_info.get("phone", "")
            if not info.get("fax"):       info["fax"]       = c_info.get("fax", "")
            if not info.get("website"):   info["website"]   = c_info.get("website", "")
            if not info.get("representative"): info["representative"] = c_info.get("representative", "")
            if not info.get("position"):  info["position"]  = c_info.get("position", "")
            if not info.get("bank_account"): info["bank_account"] = c_info.get("bank_account", "")

        self.inp_legal_name.setText(info.get("legal_name", ""))
        self.inp_tax_id.setText(info.get("tax_id", ""))
        self.inp_address.setText(info.get("address", ""))
        self.inp_email.setText(info.get("email", ""))
        self.inp_phone.setText(info.get("phone", ""))
        self.inp_fax.setText(info.get("fax", ""))
        self.inp_website.setText(info.get("website", ""))
        self.inp_rep.setText(info.get("representative", ""))
        self.inp_pos.setText(info.get("position", ""))
        self.inp_bank.setText(info.get("bank_account", ""))
        self.inp_contract_format.setText(info.get("contract_format", "HD-{YYYY}{MM}{DD}-{RAND4}"))

        m_cfg = info.get("mail_config", {})
        self.inp_imap_server.setText(m_cfg.get("imap_server", "imap.gmail.com"))
        self.inp_imap_port.setText(m_cfg.get("imap_port", "993"))
        self.inp_smtp_server.setText(m_cfg.get("smtp_server", "smtp.gmail.com"))
        self.inp_smtp_port.setText(m_cfg.get("smtp_port", "465"))
        self.inp_mail_user.setText(m_cfg.get("user", ""))
        self.inp_mail_pass.setText(m_cfg.get("pass", ""))
        self._refresh_company_profile_selector(main, info.get("tax_id", ""))

        if hasattr(self, 'inp_mail_body'):
            saved_body = info.get("mail_body", "")
            if saved_body:
                self.inp_mail_body.setPlainText(saved_body)

        signature_options = info.get("signature_options", {})
        if not isinstance(signature_options, dict):
            signature_options = {}
        if hasattr(self, "chk_signature_hide_logo"):
            self.chk_signature_hide_logo.setChecked(bool(signature_options.get("hide_logo", False)))
        if hasattr(self, "chk_signature_hide_background"):
            self.chk_signature_hide_background.setChecked(bool(signature_options.get("hide_background", False)))
        if hasattr(self, "chk_signature_hide_border"):
            self.chk_signature_hide_border.setChecked(bool(signature_options.get("hide_border", False)))
        if hasattr(self, "chk_signature_hide_check"):
            self.chk_signature_hide_check.setChecked(bool(signature_options.get("hide_check", False)))

        self._refresh_signature_asset_previews()

        if hasattr(main, "is_cloud_active"):
            self.refresh_activation(main)



    def refresh_activation(self, main):
        """Cập nhật UI thông tin bản quyền."""
        if not hasattr(self, "lbl_license_status_big"):
            return

        from core.licensing import format_plan_label, get_effective_plan_limits

        def _limit_text(plan_id: str) -> str:
            try:
                license_context = getattr(main, "license_context", {}) if main else {}
                limits = getattr(main, "effective_plan_limits", {}) or get_effective_plan_limits(plan_id, license_context)
            except Exception:
                return "Không xác định"

            contract_limit = "Không giới hạn" if limits.get("max_contracts") is None else str(limits.get("max_contracts"))
            device_limit = "Không giới hạn" if limits.get("max_devices") is None else str(limits.get("max_devices"))
            report_text = "Có" if limits.get("has_report") else "Không"
            backup_text = "Có" if limits.get("has_backup_sync") else "Không"
            mst_limit = str(limits.get("multi_mst", 1))
            profile_count = len([
                profile for profile in getattr(main, "company_profiles", [])
                if isinstance(profile, dict) and (profile.get("tax_id") or "").strip()
            ])
            if profile_count <= 0 and getattr(main, "company_info", {}).get("tax_id"):
                profile_count = 1
            return (
                f"HĐ: {contract_limit} | Thiết bị: {device_limit} | Báo cáo: {report_text} | "
                f"Backup đồng bộ: {backup_text} | Multi-MST: {profile_count}/{mst_limit}"
            )

        plan_label = format_plan_label(getattr(main, "plan_type", "free"))
        expires_at = getattr(main, "expires_at", None)
        expiry_text = expires_at.strftime("%d/%m/%Y") if expires_at else "Theo chính sách gói"

        tax_id = ""
        if hasattr(main, "company_info") and isinstance(main.company_info, dict):
            tax_id = (main.company_info.get("tax_id") or "").strip()
        if not tax_id and hasattr(main, "cloud_company_info") and isinstance(main.cloud_company_info, dict):
            tax_id = (main.cloud_company_info.get("tax_id") or "").strip()

        identity_text = f"MST: {tax_id}" if tax_id else "MST: Chưa đăng ký"
        saved_key = str(getattr(main, "license_key", "") or "").strip()
        if saved_key:
            key_tail = saved_key[-8:] if len(saved_key) > 8 else saved_key
            identity_text = f"{identity_text} | Key: ...{key_tail}"
        if hasattr(self, "inp_license_key") and self.inp_license_key.text().strip() != saved_key:
            self.inp_license_key.setText(saved_key)

        if hasattr(self, "lbl_license_plan"):
            self.lbl_license_plan.setText(plan_label)
        if hasattr(self, "lbl_license_expiry"):
            self.lbl_license_expiry.setText(expiry_text)
        if hasattr(self, "lbl_license_identity"):
            self.lbl_license_identity.setText(identity_text)
        if hasattr(self, "lbl_license_limits"):
            self.lbl_license_limits.setText(_limit_text(getattr(main, "plan_type", "free")))
            license_context = getattr(main, "license_context", {}) if main else {}
            if isinstance(license_context, dict) and license_context:
                limits_text = self.lbl_license_limits.text()
                device_limit = "KhÃ´ng giá»›i háº¡n"
                try:
                    limits = getattr(main, "effective_plan_limits", {}) or get_effective_plan_limits(
                        getattr(main, "plan_type", "free"),
                        license_context,
                    )
                    if limits.get("max_devices") is not None:
                        device_limit = str(limits.get("max_devices"))
                except Exception:
                    pass
                extra_parts = []
                try:
                    from core.contracts_db import count_contracts_total

                    current_contracts = count_contracts_total()
                    contract_limit = "KhÃ´ng giá»›i háº¡n"
                    if limits.get("max_contracts") is not None:
                        contract_limit = str(limits.get("max_contracts"))
                    extra_parts.append(f"HÄ Ä‘ang dÃ¹ng: {current_contracts}/{contract_limit}")
                except Exception:
                    pass
                if license_context.get("current_devices") is not None:
                    extra_parts.append(f"Thiáº¿t bá»‹ Ä‘ang dÃ¹ng: {license_context.get('current_devices')}/{device_limit}")
                addon_contracts = int(license_context.get("addon_contracts") or 0)
                addon_devices = int(license_context.get("addon_devices") or 0)
                addon_mst = int(license_context.get("addon_mst") or 0)
                addon_parts = []
                if addon_contracts:
                    addon_parts.append(f"+{addon_contracts} HÄ")
                if addon_devices:
                    addon_parts.append(f"+{addon_devices} thiáº¿t bá»‹")
                if addon_mst:
                    addon_parts.append(f"+{addon_mst} MST")
                if addon_parts:
                    extra_parts.append("Add-on server: " + ", ".join(addon_parts))
                if extra_parts:
                    self.lbl_license_limits.setText(limits_text + " | " + " | ".join(extra_parts))

        if getattr(main, "is_license_blocked", False):
            self.lbl_license_status_big.setText("🔒 Phần mềm đã bị khóa trên server")
            self.lbl_license_status_big.setStyleSheet("color: #b91c1c; font-size: 18px; font-weight: 700;")
            self.lbl_license_details.setText(
                getattr(main, "license_block_reason", "Bản quyền hiện tại đã bị vô hiệu hóa trên server. Vui lòng liên hệ hỗ trợ.")
            )
            if hasattr(self, "lbl_license_plan"):
                self.lbl_license_plan.setText("BỊ KHÓA")
            if hasattr(self, "lbl_license_expiry"):
                self.lbl_license_expiry.setText("Không khả dụng")
            if hasattr(self, "lbl_license_limits"):
                self.lbl_license_limits.setText("Tính năng đã bị khóa từ máy chủ bản quyền.")
        elif main.is_expired:
            self.lbl_license_status_big.setText("🔴 Bản quyền của bạn đã hết hạn")
            self.lbl_license_status_big.setStyleSheet("color: #b91c1c; font-size: 18px; font-weight: 700;")
            self.lbl_license_details.setText("Vui lòng gia hạn để tiếp tục sử dụng đầy đủ tính năng.")
            if hasattr(self, "lbl_license_expiry") and expires_at:
                self.lbl_license_expiry.setText(f"Đã hết hạn từ {expires_at.strftime('%d/%m/%Y')}")
        elif main.is_cloud_active:
            if plan_label.startswith("FREE"):
                status_text = "🟢 Bạn đang sử dụng gói FREE"
                detail_text = "Gói mặc định đang hoạt động với giới hạn cơ bản của phần mềm."
            elif plan_label.startswith("TRIAL"):
                status_text = "🟡 Bạn đang sử dụng bản Trial"
                detail_text = "Có thể nâng cấp bất kỳ lúc nào để mở thêm tính năng nâng cao."
            else:
                status_text = f"✅ Gói {plan_label} đang hoạt động"
                detail_text = "Bản quyền hợp lệ và đã đồng bộ theo MST hiện tại."
            self.lbl_license_status_big.setText(status_text)
            self.lbl_license_status_big.setStyleSheet("color: #166534; font-size: 18px; font-weight: 700;")
            self.lbl_license_details.setText(detail_text)
            if hasattr(self, "lbl_license_expiry") and not expires_at:
                self.lbl_license_expiry.setText("Vĩnh viễn")
        else:
            self.lbl_license_status_big.setText("Gói FREE mặc định")
            self.lbl_license_status_big.setStyleSheet("color: #b45309; font-size: 18px; font-weight: 700;")
            self.lbl_license_details.setText("Vui lòng hoàn tất Thông tin công ty & MST để đồng bộ gói FREE mặc định.")
            if hasattr(self, "lbl_license_plan"):
                self.lbl_license_plan.setText("FREE")
            if hasattr(self, "lbl_license_expiry"):
                self.lbl_license_expiry.setText("Không giới hạn")
            if hasattr(self, "lbl_license_limits"):
                self.lbl_license_limits.setText(_limit_text("free"))

        # Hiển thị thông báo từ Cloud (Supabase)
        if hasattr(main, "cloud_notification") and main.cloud_notification:
            self.lbl_cloud_noti.setText(f"Thông báo: {main.cloud_notification}")
            self.lbl_cloud_noti.show()
        else:
            self.lbl_cloud_noti.hide()
