import time

from PySide6.QtCore import Qt, QTimer, Signal
from PySide6.QtWidgets import (
    QWidget,
    QVBoxLayout,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QTableWidget,
    QTableWidgetItem,
    QPushButton,
    QMessageBox,
    QHeaderView,
)

from pypos.core.base_view import BaseView
from pypos.core.utils.worker_pool_utils import submit_ui_query_task_keyed


# upgraded: inherit base class
class PembatalanTransaksiView(BaseView, QWidget):
    # edited by glg
    # Bridge signal agar hasil query background di-apply aman pada UI thread.
    refresh_payload_ready = Signal(int, object)

    def __init__(self, controller, parent=None):
        super().__init__(parent)
        self.controller = controller
        self.transaksi_id_selected = None
        # edited by glg
        # Debounce pencarian agar query DB tidak dipanggil setiap ketikan.
        self._search_debounce_timer = QTimer(self)
        self._search_debounce_timer.setSingleShot(True)
        self._search_debounce_timer.setInterval(280)
        self._search_debounce_timer.timeout.connect(self.refresh_data)
        # edited by glg
        # Cache ringan refresh agar query identik tidak dipanggil berulang
        # saat user cepat berpindah tab/menu.
        self._refresh_cache_key = ""
        self._refresh_cache_ts_ms = 0.0
        self._refresh_cache_ttl_ms = 600
        self._refresh_request_seq = 0
        self.refresh_payload_ready.connect(self._on_refresh_payload_ready, Qt.QueuedConnection)

        layout = QVBoxLayout(self)

        # Top bar: search & info
        top_bar = QHBoxLayout()
        top_bar.addWidget(QLabel("Cari Nota:"))
        self.search_edit = QLineEdit()
        self.search_edit.setPlaceholderText("Cari nomor / tanggal / customer...")
        self.search_edit.textChanged.connect(self._schedule_refresh_data)
        top_bar.addWidget(self.search_edit)
        layout.addLayout(top_bar)

        # Info batas hari
        self.info_batas = QLabel("")
        layout.addWidget(self.info_batas)

        # Table transaksi
        self.tbl_master = QTableWidget(0, 7)
        self.tbl_master.setHorizontalHeaderLabels(
            ["ID", "Nomer", "Tanggal", "Customer", "Kasir", "Item", "Total"]
        )
        self.tbl_master.setSelectionBehavior(QTableWidget.SelectRows)
        self.tbl_master.setEditTriggers(QTableWidget.NoEditTriggers)
        self.tbl_master.cellClicked.connect(self._on_row_clicked)
        self.tbl_master.setColumnHidden(0, True)
        header = self.tbl_master.horizontalHeader()
        header.setStretchLastSection(False)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)  # Nomer
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)  # Tanggal
        header.setSectionResizeMode(3, QHeaderView.ResizeToContents)  # Customer
        header.setSectionResizeMode(4, QHeaderView.ResizeToContents)  # Kasir
        header.setSectionResizeMode(5, QHeaderView.ResizeToContents)  # Item
        header.setSectionResizeMode(6, QHeaderView.ResizeToContents)  # Total
        layout.addWidget(self.tbl_master)

        # Action buttons
        btn_bar = QHBoxLayout()
        self.btn_batalkan = QPushButton("Batalkan Transaksi")
        self.btn_batalkan.setProperty("class", "danger")
        self.btn_batalkan.clicked.connect(self._on_batalkan_clicked)
        btn_bar.addStretch()
        btn_bar.addWidget(self.btn_batalkan)
        layout.addLayout(btn_bar)

        self.refresh_data(force=True)

    # edited by glg
    def _schedule_refresh_data(self, _text=""):
        if hasattr(self, "_search_debounce_timer") and self._search_debounce_timer is not None:
            self._search_debounce_timer.start()

    # edited by glg
    def _next_refresh_request_id(self):
        self._refresh_request_seq += 1
        return int(self._refresh_request_seq)

    # edited by glg
    def _build_refresh_payload(self, keyword):
        rows = self.controller.load_transaksi(keyword)
        start_date, end_date = self.controller.get_allowed_date_range()
        return {
            "rows": list(rows or []),
            "start_date_text": start_date.strftime("%Y-%m-%d"),
            "end_date_text": end_date.strftime("%Y-%m-%d"),
        }

    def refresh_data(self, force=False):
        keyword = str(self.search_edit.text() or "")
        cache_key = keyword.strip().lower()
        now_ms = float(time.monotonic() * 1000.0)
        if (
            not bool(force)
            and cache_key == str(self._refresh_cache_key or "")
            and (now_ms - float(self._refresh_cache_ts_ms or 0.0)) < float(self._refresh_cache_ttl_ms)
        ):
            return

        if bool(force):
            payload = self._build_refresh_payload(keyword)
            payload["cache_key"] = cache_key
            payload["cache_ts_ms"] = now_ms
            self._apply_refresh_payload(payload)
            return

        request_id = self._next_refresh_request_id()

        def _worker():
            started_at = time.perf_counter()
            payload = self._build_refresh_payload(keyword)
            payload["cache_key"] = cache_key
            payload["cache_ts_ms"] = now_ms
            payload["elapsed_ms"] = (time.perf_counter() - started_at) * 1000.0
            self.refresh_payload_ready.emit(int(request_id), payload)

        submit_ui_query_task_keyed(f"pembatalan-master:{id(self)}", _worker)

    # edited by glg
    def _on_refresh_payload_ready(self, request_id, payload):
        if int(request_id or 0) != int(self._refresh_request_seq or 0):
            return
        data = payload if isinstance(payload, dict) else {}
        self._apply_refresh_payload(data)
        elapsed_ms = float(data.get("elapsed_ms") or 0.0)
        if elapsed_ms >= 180.0:
            self.log_info(f"[PERF] pembatalan_refresh_async elapsed_ms={elapsed_ms:.1f}")

    # edited by glg
    def _apply_refresh_payload(self, payload):
        data = payload if isinstance(payload, dict) else {}
        rows = data.get("rows") or []
        start_date_text = str(data.get("start_date_text") or "")
        end_date_text = str(data.get("end_date_text") or "")
        self.info_batas.setText(
            f"Hanya transaksi tanggal {start_date_text} s/d {end_date_text} yang bisa dibatalkan."
        )
        self.tbl_master.setUpdatesEnabled(False)
        try:
            self.tbl_master.setRowCount(len(rows))
            for r, row in enumerate(rows):
                self.tbl_master.setItem(r, 0, QTableWidgetItem(str(row["id"])))
                self.tbl_master.setItem(r, 1, QTableWidgetItem(row["nomer"]))
                self.tbl_master.setItem(r, 2, QTableWidgetItem(row["dtime"]))
                self.tbl_master.setItem(r, 3, QTableWidgetItem(row["customers_nama"]))
                self.tbl_master.setItem(r, 4, QTableWidgetItem(str(row["kasir_nama"] or "")))
                self.tbl_master.setItem(r, 5, QTableWidgetItem(str(row["jumlah_item"] or 0)))
                self.tbl_master.setItem(r, 6, QTableWidgetItem(f"{row['transaksi_nilai']:,.0f}"))
            self.transaksi_id_selected = None
        finally:
            self.tbl_master.setUpdatesEnabled(True)

        self._refresh_cache_key = str(data.get("cache_key") or "")
        self._refresh_cache_ts_ms = float(data.get("cache_ts_ms") or 0.0)

    def _on_row_clicked(self, row, _col):
        id_item = self.tbl_master.item(row, 0)
        self.transaksi_id_selected = id_item.text() if id_item else None

    def _on_batalkan_clicked(self):
        if not self.transaksi_id_selected:
            QMessageBox.warning(self, "Pembatalan", "Pilih transaksi yang akan dibatalkan.")
            return
        confirm = QMessageBox(self)
        confirm.setIcon(QMessageBox.Warning)
        confirm.setWindowTitle("Konfirmasi")
        confirm.setText("Batalkan transaksi ini? Tindakan ini akan menghapus satu nota penuh.")
        confirm.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        confirm.setDefaultButton(QMessageBox.No)
        if confirm.exec() != QMessageBox.Yes:
            return
        admin_name = self.controller.verifikasi_admin_pembatalan(self)
        if not admin_name:
            return
        ok, msg = self.controller.batalkan_transaksi(self.transaksi_id_selected, admin_name=admin_name)
        if ok:
            QMessageBox.information(self, "Pembatalan", msg)
            # edited by glg
            # Invalidasi cache sebelum refresh agar kompatibel dengan spy test
            # yang melakukan monkeypatch method tanpa parameter.
            self._refresh_cache_key = ""
            self._refresh_cache_ts_ms = 0.0
            self.refresh_data()
            main_window = self.window()
            if hasattr(main_window, "buka_penjualan"):
                main_window.buka_penjualan()
        else:
            QMessageBox.warning(self, "Pembatalan", msg)
