from pypos.core.base_controller import BaseController
from pypos.modules.dashboard.models.dashboard_info_model import DashboardInfoModel
from pypos.modules.dashboard.views.dashboard_info_view import DashboardInfoView
from PySide6.QtCore import QObject, QTimer, Qt, Signal
import time
import threading
from pypos.core.utils.config_utils import read_config
from pypos.core.utils.worker_pool_utils import submit_ui_periodic_task_keyed


# edited by glg
# Bridge signal agar hasil query background masuk aman ke UI thread.
class _DashboardInfoSignalBridge(QObject):
    summary_ready = Signal(object)


class DashboardInfoController(BaseController):
    def __init__(self, db_path):
        super().__init__()
        self.model = DashboardInfoModel(db_path)
        self.view = DashboardInfoView()
        # edited by glg
        # Refresh ringkasan dashboard diperlambat agar query periodik tidak terlalu menekan UI thread.
        cfg = read_config()
        try:
            refresh_interval_ms = int(cfg.get("dashboard_info_refresh_interval_ms", 15000))
        except (TypeError, ValueError, KeyError, AttributeError, RuntimeError, OSError, LookupError, ArithmeticError, ImportError):
            refresh_interval_ms = 15000
        self._refresh_interval_ms = max(5000, refresh_interval_ms)
        self._perf_warn_threshold_ms = 200.0
        # edited by glg
        # Guard agar refresh periodik tidak overlap saat query lambat.
        self._refresh_lock = threading.Lock()
        self._refresh_inflight = False
        self._signal_bridge = _DashboardInfoSignalBridge(self.view)
        self._signal_bridge.summary_ready.connect(
            self._on_summary_ready,
            Qt.QueuedConnection,
        )
        self.timer = QTimer(self.view)
        self.timer.setInterval(self._refresh_interval_ms)
        self.timer.timeout.connect(self.refresh_data)
        self.timer.start()
        self.refresh_data()

    def update_info(self):
        transaksi_count, transaksi_total, retur_count, retur_total = self.model.get_today_summary()
        self.view.update_info(transaksi_count, transaksi_total, retur_count, retur_total)

    def get_view(self):
        return self.view

    def refresh_data(self):
        with self._refresh_lock:
            if self._refresh_inflight:
                return
            self._refresh_inflight = True
        started_event = threading.Event()

        def _worker():
            started_event.set()
            started_at = time.perf_counter()
            payload = {
                "ok": True,
                "summary": (0, 0, 0, 0),
                "elapsed_ms": 0.0,
                "interval_ms": int(self._refresh_interval_ms),
                "error": "",
            }
            try:
                payload["summary"] = tuple(self.model.get_today_summary() or (0, 0, 0, 0))
            except (TypeError, ValueError, KeyError, AttributeError, RuntimeError, OSError, LookupError, ArithmeticError, ImportError) as exc:
                payload["ok"] = False
                payload["error"] = str(exc)
            finally:
                payload["elapsed_ms"] = (time.perf_counter() - started_at) * 1000.0

            try:
                self._signal_bridge.summary_ready.emit(payload)
            except (TypeError, ValueError, KeyError, AttributeError, RuntimeError, OSError, LookupError, ArithmeticError, ImportError):
                with self._refresh_lock:
                    self._refresh_inflight = False

        try:
            key = f"dashboard-info-refresh:{id(self)}"
            future = submit_ui_periodic_task_keyed(key, _worker)
            if future is None:
                with self._refresh_lock:
                    self._refresh_inflight = False
                return

            # edited by glg
            # Fail-safe antrean worker penuh: task periodik bisa ter-drop.
            # Lepaskan inflight agar refresh berikutnya tetap berjalan.
            def _on_done(_):
                if started_event.is_set():
                    return
                with self._refresh_lock:
                    self._refresh_inflight = False

            try:
                future.add_done_callback(_on_done)
            except (TypeError, ValueError, KeyError, AttributeError, RuntimeError, OSError, LookupError, ArithmeticError, ImportError):
                if not started_event.is_set():
                    with self._refresh_lock:
                        self._refresh_inflight = False
        except (TypeError, ValueError, KeyError, AttributeError, RuntimeError, OSError, LookupError, ArithmeticError, ImportError):
            with self._refresh_lock:
                self._refresh_inflight = False

    # edited by glg
    def _on_summary_ready(self, payload):
        with self._refresh_lock:
            self._refresh_inflight = False

        data = payload if isinstance(payload, dict) else {}
        if not bool(data.get("ok")):
            self.log_warning(
                f"Gagal refresh ringkasan dashboard: {str(data.get('error') or 'unknown_error')}"
            )
            return

        summary = data.get("summary") or (0, 0, 0, 0)
        if len(summary) < 4:
            return
        self.view.update_info(summary[0], summary[1], summary[2], summary[3])

        elapsed_ms = float(data.get("elapsed_ms") or 0.0)
        if elapsed_ms >= self._perf_warn_threshold_ms:
            self.log_info(
                f"[PERF] dashboard_info_refresh elapsed_ms={elapsed_ms:.1f} "
                f"interval_ms={int(data.get('interval_ms') or self._refresh_interval_ms)}"
            )
