"""
customers_module.py — Quản lý Khách Hàng & Nhà Cung Cấp (CRUD)
Dùng bảng `partners` trong company DB.
"""
import os, sqlite3
from PySide6.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
    QTableWidget, QTableWidgetItem, QLineEdit, QDialog,
    QFormLayout, QComboBox, QTextEdit, QMessageBox, QHeaderView,
    QSizePolicy, QFrame, QApplication
)
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QFont, QIcon, QColor

from gui.ui_helpers import (
    PrimaryButton, SecondaryButton, DangerButton, GhostButton,
    make_input, make_combo, show_toast, _load_icon, PageHeader
)
from gui.theme import COLORS

_ASSETS = os.path.join(os.path.dirname(os.path.dirname(__file__)), "assets")


# ──────────────────────────────────────────────────────────────────────────
# Dialog thêm / sửa partner
# ──────────────────────────────────────────────────────────────────────────
class PartnerDialog(QDialog):
    """Dialog nhập thông tin khách hàng / nhà cung cấp."""

    def __init__(self, parent=None, data: dict = None, db_path: str = ""):
        super().__init__(parent)
        self.setWindowTitle("Thêm Khách Hàng" if not data else "Sửa Khách Hàng")
        self.setMinimumWidth(520)
        self.setModal(True)
        self._db_path = db_path

        self._data = data or {}
        layout = QVBoxLayout(self)
        layout.setSpacing(12)
        layout.setContentsMargins(20, 16, 20, 16)

        # ── Fix màu nền + label cho dialog ────────────────────────────
        self.setStyleSheet(f"""
            QDialog {{
                background-color: {COLORS['bg_white']};
            }}
            QLabel {{
                color: {COLORS['text_primary']};
                background-color: transparent;
                font-size: 10pt;
            }}
            QLineEdit, QComboBox, QTextEdit {{
                color: {COLORS['text_primary']};
                background-color: {COLORS['bg_white']};
                border: 1px solid {COLORS['border']};
                border-radius: 6px;
                padding: 0px 8px;
                min-height: 32px;
            }}
            QLineEdit:focus, QComboBox:focus {{
                border: 1px solid {COLORS['accent']};
            }}
            QTextEdit {{
                padding: 6px 8px;
                min-height: 60px;
            }}
        """)

        # ── Form fields ────────────────────────────────────────────────
        form = QFormLayout()
        form.setSpacing(8)
        form.setLabelAlignment(Qt.AlignmentFlag.AlignRight)

        self.inp_code    = make_input("VD: KH001")
        self.inp_name    = make_input("Tên công ty / cá nhân")
        self.inp_tax     = make_input("Mã số thuế")
        self.inp_type    = make_combo(["Khách hàng", "Nhà cung cấp", "Cả hai"])
        # Cập nhật mã nội bộ khi mới thêm (không có data cũ)
        if not data:
            self.inp_type.currentIndexChanged.connect(self._update_code_prefix)
            self.inp_code.setReadOnly(True)
            self.inp_code.setPlaceholderText("Tự sinh")
        self.inp_address = make_input("Địa chỉ")
        self.inp_phone   = make_input("Số điện thoại")
        self.inp_email   = make_input("Email")
        self.inp_rep     = make_input("Người đại diện")
        self.inp_pos     = make_input("Chức vụ đại diện")
        self.inp_bank_acc= make_input("Số tài khoản ngân hàng")
        self.inp_bank_nm = make_input("Tên ngân hàng")
        self.inp_notes   = QTextEdit()
        self.inp_notes.setPlaceholderText("Ghi chú...")
        self.inp_notes.setMaximumHeight(70)

        form.addRow("Mã nội bộ (tự sinh):", self.inp_code)
        form.addRow("Tên *:", self.inp_name)

        # Tra cứu MST row
        mst_row = QHBoxLayout()
        mst_row.addWidget(self.inp_tax)
        btn_lookup = SecondaryButton("🔍 Tra cứu MST")
        btn_lookup.setFixedWidth(130)
        btn_lookup.clicked.connect(self._lookup_mst)
        mst_row.addWidget(btn_lookup)

        form.addRow("MST:", mst_row)
        form.addRow("Phân loại:", self.inp_type)
        form.addRow("Địa chỉ:", self.inp_address)
        form.addRow("Điện thoại:", self.inp_phone)
        form.addRow("Email:", self.inp_email)
        form.addRow("Đại diện:", self.inp_rep)
        form.addRow("Chức vụ:", self.inp_pos)
        form.addRow("Số TK ngân hàng:", self.inp_bank_acc)
        form.addRow("Ngân hàng:", self.inp_bank_nm)
        form.addRow("Ghi chú:", self.inp_notes)

        layout.addLayout(form)

        # ── Nút hành động ─────────────────────────────────────────────
        btn_row = QHBoxLayout()
        btn_row.addStretch()
        btn_cancel = GhostButton("Hủy")
        btn_cancel.setFixedWidth(80)
        btn_save = PrimaryButton("💾 Lưu")
        btn_save.setFixedWidth(110)
        btn_cancel.clicked.connect(self.reject)
        btn_save.clicked.connect(self._validate_and_accept)
        btn_row.addWidget(btn_cancel)
        btn_row.addWidget(btn_save)
        layout.addLayout(btn_row)

        # ── Điền dữ liệu cũ nếu sửa ──────────────────────────────────
        self._fill_fields()

    # ── Auto-gen mã nội bộ ──────────────────────────────────────────
    def _gen_code(self, prefix: str = "KH") -> str:
        """Sinh mã tiếp theo: KH001, KH002... dựa trên số lớn nhất trong DB."""
        if not self._db_path:
            return ""
        try:
            conn = sqlite3.connect(self._db_path)
            start_pos = len(prefix) + 1
            max_num = conn.execute(
                """
                SELECT MAX(CAST(SUBSTR(code, ?) AS INTEGER))
                FROM partners
                WHERE code LIKE ?
                """,
                (start_pos, f"{prefix}%")
            ).fetchone()[0]
            conn.close()
            next_num = (max_num or 0) + 1
            return f"{prefix}{next_num:03d}"
        except Exception:
            return ""

    def _type_prefix(self) -> str:
        return ["KH", "NCC", "KH"][self.inp_type.currentIndex()]

    def _update_code_prefix(self):
        """Cập nhật prefix khi đổi loại (chỉ khi mã chưa nhập tay)."""
        prefix = self._type_prefix()
        self.inp_code.setText(self._gen_code(prefix))

    def ensure_auto_code(self) -> str:
        """Đảm bảo luôn có mã nội bộ khi thêm mới."""
        code = (self.inp_code.text() or "").strip()
        if code:
            return code
        code = self._gen_code(self._type_prefix())
        self.inp_code.setText(code)
        return code

    def _fill_fields(self):
        d = self._data
        if not d:
            # Móc mới: tự sinh mã
            self._update_code_prefix()
            return
        self.inp_code.setText(d.get("code", ""))
        self.inp_name.setText(d.get("name", ""))
        self.inp_tax.setText(d.get("tax_code", ""))
        type_map = {"customer": 0, "vendor": 1, "both": 2}
        self.inp_type.setCurrentIndex(type_map.get(d.get("type", "both"), 2))
        self.inp_address.setText(d.get("address", ""))
        self.inp_phone.setText(d.get("phone", ""))
        self.inp_email.setText(d.get("email", ""))
        self.inp_rep.setText(d.get("representative", ""))
        self.inp_pos.setText(d.get("position", ""))
        self.inp_bank_acc.setText(d.get("bank_account", ""))
        self.inp_bank_nm.setText(d.get("bank_name", ""))
        self.inp_notes.setPlainText(d.get("notes", ""))

    def _lookup_mst(self):
        mst = self.inp_tax.text().strip()
        if not mst:
            QMessageBox.warning(self, "Thiếu MST", "Vui lòng nhập mã số thuế trước.")
            return
        try:
            show_toast(self.window(), f"⏳ Đang tra cứu MST {mst}...")
            QApplication.processEvents()
            from core.api_mst import lookup_mst
            biz = lookup_mst(mst)
            if biz:
                if biz.get("name"):
                    self.inp_name.setText(biz.get("name"))
                if biz.get("address"):
                    self.inp_address.setText(biz.get("address"))
                show_toast(self.window(), f"✅ Tìm thấy: {biz.get('name', '')}")
            else:
                QMessageBox.information(self, "Không tìm thấy", f"Không có kết quả cho MST: {mst}")
        except Exception as e:
            QMessageBox.warning(self, "Lỗi", f"Tra cứu thất bại: {e}")

    def _validate_and_accept(self):
        if not self.inp_name.text().strip():
            QMessageBox.warning(self, "Thiếu thông tin", "Vui lòng nhập Tên khách hàng.")
            self.inp_name.setFocus()
            return
        self.accept()

    def get_values(self) -> dict:
        type_vals = ["customer", "vendor", "both"]
        return {
            "code":           self.inp_code.text().strip() or None,
            "name":           self.inp_name.text().strip(),
            "tax_code":       self.inp_tax.text().strip() or None,
            "type":           type_vals[self.inp_type.currentIndex()],
            "address":        self.inp_address.text().strip() or None,
            "phone":          self.inp_phone.text().strip() or None,
            "email":          self.inp_email.text().strip() or None,
            "representative": self.inp_rep.text().strip() or None,
            "position":       self.inp_pos.text().strip() or None,
            "bank_account":   self.inp_bank_acc.text().strip() or None,
            "bank_name":      self.inp_bank_nm.text().strip() or None,
            "notes":          self.inp_notes.toPlainText().strip() or None,
            "is_active":      1,
        }


# ──────────────────────────────────────────────────────────────────────────
# Dialog chọn khách hàng (dùng ở form Hợp Đồng)
# ──────────────────────────────────────────────────────────────────────────
class CustomerPickerDialog(QDialog):
    """Popup chọn khách hàng từ danh sách partners."""

    customer_selected = Signal(dict)

    def __init__(self, db_path: str, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Chọn Khách Hàng")
        self.setMinimumSize(700, 420)
        self.db_path = db_path
        self._rows = []

        layout = QVBoxLayout(self)
        layout.setSpacing(8)

        # Search
        search_row = QHBoxLayout()
        self.inp_search = make_input("Tìm theo tên hoặc MST...")
        self.inp_search.textChanged.connect(self._filter)
        search_row.addWidget(QLabel("🔍"))
        search_row.addWidget(self.inp_search)
        layout.addLayout(search_row)

        # Table
        self.table = QTableWidget(0, 5)
        self.table.setHorizontalHeaderLabels(["Mã", "Tên", "MST", "Loại", "Địa chỉ"])
        self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
        self.table.horizontalHeader().setSectionResizeMode(4, QHeaderView.ResizeMode.Stretch)
        self.table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
        self.table.setSelectionMode(QTableWidget.SelectionMode.SingleSelection)
        self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
        self.table.setAlternatingRowColors(True)
        self.table.setShowGrid(False)
        self.table.setStyleSheet(
            "QTableWidget {"
            "  border: 1px solid #dbe2ea;"
            "  border-radius: 8px;"
            "  background: #ffffff;"
            "  alternate-background-color: #f8fbff;"
            "}"
            "QTableWidget::item {"
            "  padding: 6px;"
            "}"
            "QTableWidget::item:selected {"
            "  background: #e7f0ff;"
            "  color: #0f172a;"
            "  border: none;"
            "}"
            "QHeaderView::section {"
            "  background: #f3f6fb;"
            "  color: #334155;"
            "  border: none;"
            "  border-bottom: 1px solid #dbe2ea;"
            "  padding: 6px;"
            "  font-weight: 600;"
            "}"
        )
        self.table.doubleClicked.connect(self._on_select)
        layout.addWidget(self.table)

        # Buttons
        btn_row = QHBoxLayout()
        btn_row.addStretch()
        btn_cancel = GhostButton("Hủy")
        btn_cancel.clicked.connect(self.reject)
        btn_select = PrimaryButton("✅ Chọn")
        btn_select.clicked.connect(self._on_select)
        btn_row.addWidget(btn_cancel)
        btn_row.addWidget(btn_select)
        layout.addLayout(btn_row)

        self._load_data()

    def _load_data(self):
        if not self.db_path or not os.path.exists(self.db_path):
            return
        try:
            try:
                from core.db_init import init_company_db
                init_company_db(self.db_path)
            except Exception:
                pass
            conn = sqlite3.connect(self.db_path)
            conn.row_factory = sqlite3.Row
            self._rows = [dict(r) for r in conn.execute(
                "SELECT * FROM partners WHERE is_active=1 ORDER BY name"
            ).fetchall()]
            conn.close()
        except Exception as e:
            print(f"[CustomerPicker] load error: {e}")
        self._render(self._rows)

    def _render(self, rows):
        type_label = {"customer": "Khách hàng", "vendor": "NCC", "both": "KH + NCC"}
        self.table.setRowCount(0)
        for r in rows:
            row = self.table.rowCount()
            self.table.insertRow(row)
            self.table.setItem(row, 0, QTableWidgetItem(r.get("code") or ""))
            self.table.setItem(row, 1, QTableWidgetItem(r.get("name") or ""))
            self.table.setItem(row, 2, QTableWidgetItem(r.get("tax_code") or ""))
            self.table.setItem(row, 3, QTableWidgetItem(type_label.get(r.get("type", ""), "")))
            self.table.setItem(row, 4, QTableWidgetItem(r.get("address") or ""))

    def _filter(self, text: str):
        kw = text.lower()
        filtered = [r for r in self._rows if
                    kw in (r.get("name") or "").lower() or
                    kw in (r.get("tax_code") or "").lower()]
        self._render(filtered)

    def _on_select(self):
        row = self.table.currentRow()
        if row < 0:
            return
        # Find matching record
        name = self.table.item(row, 1).text()
        mst  = self.table.item(row, 2).text()
        matched = next((r for r in self._rows if r.get("name") == name and r.get("tax_code") == mst), None)
        if matched:
            self.customer_selected.emit(matched)
        self.accept()


# ──────────────────────────────────────────────────────────────────────────
# Trang chính — CustomersPage
# ──────────────────────────────────────────────────────────────────────────
class CustomersPage(QWidget):
    """Module quản lý Khách Hàng & Nhà Cung Cấp — CRUD đầy đủ."""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.db_path = ""
        self._all_rows: list[dict] = []
        self._build_ui()

    # ── Build UI ──────────────────────────────────────────────────────

    def _build_ui(self):
        main = QVBoxLayout(self)
        main.setContentsMargins(20, 16, 20, 16)
        main.setSpacing(12)

        # ── Toolbar ───────────────────────────────────────────────────
        toolbar = QHBoxLayout()
        toolbar.setSpacing(8)

        self.inp_search = make_input("🔍  Tìm theo tên, MST, địa chỉ...")
        self.inp_search.textChanged.connect(self._on_search)
        self.inp_search.setFixedHeight(36)

        self.filter_type = make_combo(["Tất cả", "Khách hàng", "Nhà cung cấp", "Cả hai"])
        self.filter_type.setFixedWidth(160)
        self.filter_type.currentIndexChanged.connect(lambda _: self._apply_filter())

        btn_add   = PrimaryButton("➕ Thêm mới", icon=_load_icon("action_add_128.png"))
        btn_edit  = SecondaryButton("✏️ Sửa",    icon=_load_icon("action_edit_128.png"))
        btn_del   = DangerButton("🗑 Xóa",       icon=_load_icon("action_delete_128.png"))
        btn_refresh = GhostButton("↺ Làm mới",  icon=_load_icon("action_refresh_128.png"))

        btn_add.clicked.connect(self._on_add)
        btn_edit.clicked.connect(self._on_edit)
        btn_del.clicked.connect(self._on_delete)
        btn_refresh.clicked.connect(lambda: self.refresh(self.db_path))

        toolbar.addWidget(self.inp_search, stretch=1)
        toolbar.addWidget(self.filter_type)
        toolbar.addWidget(btn_add)
        toolbar.addWidget(btn_edit)
        toolbar.addWidget(btn_del)
        toolbar.addWidget(btn_refresh)
        main.addLayout(toolbar)

        # ── Table ─────────────────────────────────────────────────────
        COLS = ["Mã nội bộ", "Tên", "MST", "Phân loại", "Địa chỉ", "Điện thoại", "Email", "Ngân hàng"]
        self.table = QTableWidget(0, len(COLS))
        self.table.setHorizontalHeaderLabels(COLS)
        self.table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
        self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
        self.table.setAlternatingRowColors(True)
        self.table.verticalHeader().setVisible(False)
        self.table.setShowGrid(False)

        hh = self.table.horizontalHeader()
        hh.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
        hh.setSectionResizeMode(4, QHeaderView.ResizeMode.Stretch)
        for i in [0, 2, 3, 5, 6, 7]:
            hh.setSectionResizeMode(i, QHeaderView.ResizeMode.ResizeToContents)

        self.table.doubleClicked.connect(self._on_edit)
        main.addWidget(self.table, stretch=1)

        # ── Status bar ────────────────────────────────────────────────
        self.lbl_count = QLabel("—")
        self.lbl_count.setStyleSheet(f"color:{COLORS['text_muted']}; font-size:12px;")
        main.addWidget(self.lbl_count)

    # ── DB Helpers ────────────────────────────────────────────────────

    def _resolve_db_path(self) -> str:
        if self.db_path:
            return self.db_path

        main = self.window()
        db_path = str(getattr(main, "current_db_path", "") or "").strip()
        if not db_path and hasattr(main, "company_info") and isinstance(main.company_info, dict):
            tax_id = "".join(ch for ch in str(main.company_info.get("tax_id", "") or "") if ch.isdigit())
            if tax_id:
                try:
                    from core.company_manager import get_company_db_path_for_tax_id
                    db_path = get_company_db_path_for_tax_id(tax_id)
                except Exception:
                    db_path = ""

        if db_path:
            self.db_path = db_path
        return self.db_path

    def _conn(self):
        if not self.db_path:
            raise RuntimeError("Chưa có database công ty. Vui lòng lưu Thông tin Công ty/MST trước.")
        conn = sqlite3.connect(self.db_path)
        conn.execute("PRAGMA journal_mode=WAL")
        conn.execute("PRAGMA foreign_keys=ON")
        conn.row_factory = sqlite3.Row
        return conn

    def _ensure_cols(self) -> bool:
        """Thêm cột representative/position nếu DB cũ chưa có."""
        try:
            conn = self._conn()
            cols = [r[1] for r in conn.execute("PRAGMA table_info(partners)").fetchall()]
            if not cols:
                conn.close()
                return False
            if "representative" not in cols:
                conn.execute("ALTER TABLE partners ADD COLUMN representative TEXT")
            if "position" not in cols:
                conn.execute("ALTER TABLE partners ADD COLUMN position TEXT")
            conn.commit()
            conn.close()
            return True
        except Exception as e:
            print(f"[CustomersPage] migration error: {e}")
            return False

    def _ensure_schema(self, notify: bool = False) -> bool:
        """Tao/migrate schema company DB truoc khi thao tac voi partners."""
        db_path = self._resolve_db_path()
        if not db_path:
            if notify:
                QMessageBox.warning(
                    self,
                    "Chưa có MST",
                    "Vui lòng vào Cài đặt > Thông tin Công ty, nhập và lưu MST trước khi thêm khách hàng.",
                )
            return False
        try:
            from core.db_init import init_company_db
            init_company_db(db_path)
        except Exception as e:
            print(f"[CustomersPage] schema init error: {e}")
            if notify:
                QMessageBox.critical(self, "Lỗi database", f"Không thể khởi tạo database khách hàng:\n{e}")
            return False
        if not self._ensure_cols():
            if notify:
                QMessageBox.critical(self, "Lỗi database", "Không thể tạo bảng khách hàng trong database hiện tại.")
            return False
        return True

    def _load_db(self):
        if not self._resolve_db_path():
            return
        if not self._ensure_schema():
            self._all_rows = []
            return
        try:
            conn = self._conn()
            self._all_rows = [dict(r) for r in conn.execute(
                "SELECT * FROM partners ORDER BY name"
            ).fetchall()]
            conn.close()
        except Exception as e:
            print(f"[CustomersPage] load error: {e}")
            self._all_rows = []

    # ── Render ────────────────────────────────────────────────────────

    def _render(self, rows: list[dict]):
        type_label = {"customer": "Khách hàng", "vendor": "NCC", "both": "KH + NCC"}
        active_color = QColor(COLORS.get("success", "#22c55e"))
        inactive_color = QColor("#888888")

        self.table.setRowCount(0)
        for r in rows:
            row = self.table.rowCount()
            self.table.insertRow(row)
            vals = [
                r.get("code") or "",
                r.get("name") or "",
                r.get("tax_code") or "",
                type_label.get(r.get("type", ""), ""),
                r.get("address") or "",
                r.get("phone") or "",
                r.get("email") or "",
                r.get("bank_name") or "",
            ]
            for col, v in enumerate(vals):
                item = QTableWidgetItem(v)
                if not r.get("is_active", 1):
                    item.setForeground(inactive_color)
                self.table.setItem(row, col, item)

        total = len(self._all_rows)
        shown = len(rows)
        self.lbl_count.setText(f"Hiển thị {shown}/{total} đối tác")

    def _on_search(self, text: str):
        self._apply_filter(text)

    def _apply_filter(self, text: str = None):
        if text is None:
            text = self.inp_search.text()
        kw = text.lower()

        type_filter = self.filter_type.currentIndex()  # 0=all, 1=customer, 2=vendor, 3=both
        type_keys = [None, "customer", "vendor", "both"]

        filtered = []
        for r in self._all_rows:
            # Type filter
            if type_filter > 0 and r.get("type") != type_keys[type_filter]:
                continue
            # Text search
            searchable = " ".join([
                r.get("name") or "",
                r.get("tax_code") or "",
                r.get("address") or "",
                r.get("phone") or "",
                r.get("email") or "",
            ]).lower()
            if kw and kw not in searchable:
                continue
            filtered.append(r)
        self._render(filtered)

    # ── CRUD Actions ──────────────────────────────────────────────────

    def _on_add(self):
        if not self._ensure_schema(notify=True):
            return
        dlg = PartnerDialog(self, db_path=self.db_path)
        if dlg.exec() == QDialog.DialogCode.Accepted:
            values = dlg.get_values()
            values["code"] = dlg.ensure_auto_code() or values.get("code")
            try:
                conn = self._conn()
                insert_sql = """
                    INSERT INTO partners (code, name, tax_code, type, address, phone, email,
                        representative, position, bank_account, bank_name, notes, is_active)
                    VALUES (:code, :name, :tax_code, :type, :address, :phone, :email,
                        :representative, :position, :bank_account, :bank_name, :notes, :is_active)
                """
                # Retry 1-2 lần để tránh đụng UNIQUE trong trường hợp thêm liên tiếp.
                for _ in range(3):
                    try:
                        conn.execute(insert_sql, values)
                        break
                    except sqlite3.IntegrityError:
                        values["code"] = dlg._gen_code(dlg._type_prefix())
                else:
                    raise sqlite3.IntegrityError("Không thể tự sinh mã nội bộ không trùng")
                conn.commit()
                conn.close()
                show_toast(self.window(), f"✅ Đã thêm: {values['name']}")
                self.refresh(self.db_path)
            except sqlite3.IntegrityError as e:
                QMessageBox.warning(self, "Trùng dữ liệu", f"Mã nội bộ đã tồn tại.\n{e}")
            except Exception as e:
                QMessageBox.critical(self, "Lỗi", f"Không thể thêm: {e}")

    def _selected_id(self) -> int | None:
        row = self.table.currentRow()
        if row < 0:
            return None
        # Find by name + tax_code
        name    = self.table.item(row, 1).text() if self.table.item(row, 1) else ""
        tax     = self.table.item(row, 2).text() if self.table.item(row, 2) else ""
        for r in self._all_rows:
            if r.get("name") == name and (r.get("tax_code") or "") == tax:
                return r["id"]
        return None

    def _selected_row_data(self) -> dict | None:
        row = self.table.currentRow()
        if row < 0:
            return None
        name = self.table.item(row, 1).text() if self.table.item(row, 1) else ""
        tax  = self.table.item(row, 2).text() if self.table.item(row, 2) else ""
        for r in self._all_rows:
            if r.get("name") == name and (r.get("tax_code") or "") == tax:
                return r
        return None

    def _on_edit(self):
        if not self._ensure_schema(notify=True):
            return
        data = self._selected_row_data()
        if not data:
            show_toast(self.window(), "⚠️ Chưa chọn dòng nào để sửa.")
            return
        dlg = PartnerDialog(self, data=data, db_path=self.db_path)
        if dlg.exec() == QDialog.DialogCode.Accepted:
            values = dlg.get_values()
            values["id"] = data["id"]
            try:
                conn = self._conn()
                conn.execute("""
                    UPDATE partners SET code=:code, name=:name, tax_code=:tax_code,
                        type=:type, address=:address, phone=:phone, email=:email,
                        representative=:representative, position=:position,
                        bank_account=:bank_account, bank_name=:bank_name,
                        notes=:notes, is_active=:is_active
                    WHERE id=:id
                """, values)
                conn.commit()
                conn.close()
                show_toast(self.window(), f"✅ Đã cập nhật: {values['name']}")
                self.refresh(self.db_path)
            except Exception as e:
                QMessageBox.critical(self, "Lỗi", f"Không thể cập nhật: {e}")

    def _on_delete(self):
        if not self._ensure_schema(notify=True):
            return
        data = self._selected_row_data()
        if not data:
            show_toast(self.window(), "⚠️ Chưa chọn dòng nào để xóa.")
            return
        reply = QMessageBox.question(
            self, "Xác nhận xóa",
            f"Bạn có chắc muốn xóa '{data['name']}'?\nHành động này không thể hoàn tác.",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
            QMessageBox.StandardButton.No
        )
        if reply != QMessageBox.StandardButton.Yes:
            return
        try:
            conn = self._conn()
            conn.execute("DELETE FROM partners WHERE id=?", (data["id"],))
            conn.commit()
            conn.close()
            show_toast(self.window(), f"🗑 Đã xóa: {data['name']}")
            self.refresh(self.db_path)
        except Exception as e:
            QMessageBox.critical(self, "Lỗi", f"Không thể xóa: {e}")

    # ── Public API ────────────────────────────────────────────────────

    def refresh(self, db_path: str = ""):
        if db_path:
            self.db_path = db_path
        if not self.db_path:
            return
        self._load_db()
        self._apply_filter()
