import threading

from PySide6.QtCore import QObject, Qt, Signal

from pypos.core.base_controller import BaseController
from pypos.core.utils.worker_pool_utils import submit_ui_query_task_keyed
from pypos.modules.customer.config.customer_config import (
    get_customer_config,
    get_customer_search_page_size,
)
from pypos.modules.customer.services.customer_search_service import CustomerSearchService
from pypos.modules.customer.views.customer_search_view import CustomerSearchView


# edited by glg
# Bridge payload async agar aman diproses di UI thread.
class _CustomerSearchSignalBridge(QObject):
    page_payload_ready = Signal(int, object)


class CustomerSearchController(BaseController):
    def __init__(self, parent=None, service=None):
        super().__init__()
        self.service = service or CustomerSearchService()
        self.config = get_customer_config()
        self.page_size = get_customer_search_page_size(self.config)
        self.current_keyword = ""
        self.current_offset = 0
        self.total_count = 0
        self.customer_list = []
        self.view = CustomerSearchView(self, parent)
        # edited by glg
        # Guard stale request agar hasil query lama tidak menimpa hasil terbaru.
        self._reload_request_seq = 0
        self._reload_request_lock = threading.Lock()
        self._signal_bridge = _CustomerSearchSignalBridge(self.view)
        self._signal_bridge.page_payload_ready.connect(
            self._on_page_payload_ready,
            Qt.QueuedConnection,
        )
        self._reload_page(reset_offset=True)

    def show(self):
        self.log_debug("CustomerSearchController.show() dipanggil")
        result = self.view.exec()
        self.log_debug(f"CustomerSearchView.exec() result={result}")
        if result == CustomerSearchView.Accepted:
            return self.view.get_selected_customer_id()
        return None

    def filter_table(self, keyword):
        self.current_keyword = str(keyword or "").strip()
        self._reload_page(reset_offset=True)

    def go_next_page(self):
        if self.current_offset + self.page_size >= self.total_count:
            return
        self.current_offset += self.page_size
        self._reload_page(reset_offset=False)

    def go_prev_page(self):
        if self.current_offset <= 0:
            return
        self.current_offset = max(0, self.current_offset - self.page_size)
        self._reload_page(reset_offset=False)

    def _reload_page(self, reset_offset=False):
        if reset_offset:
            self.current_offset = 0
        keyword = str(self.current_keyword or "").strip()
        request_id = self._next_reload_request_id()
        offset = max(0, int(self.current_offset or 0))

        def _worker():
            payload = self._build_page_payload(
                keyword=keyword,
                requested_offset=offset,
            )
            try:
                self._signal_bridge.page_payload_ready.emit(int(request_id), payload)
            except (TypeError, ValueError, KeyError, AttributeError, RuntimeError, OSError, LookupError, ArithmeticError, ImportError) as exc:
                self.log_warning(f"Customer search payload emit gagal: {exc}")
                return

        submit_ui_query_task_keyed(f"customer-search-page:{id(self)}", _worker)

    # edited by glg
    def _next_reload_request_id(self):
        with self._reload_request_lock:
            self._reload_request_seq += 1
            return int(self._reload_request_seq)

    # edited by glg
    def _is_latest_reload_request(self, request_id):
        with self._reload_request_lock:
            return int(request_id or 0) == int(self._reload_request_seq or 0)

    # edited by glg
    def _build_page_payload(self, keyword, requested_offset):
        total_count = self.service.count_customers(keyword=keyword)
        resolved_offset = max(0, int(requested_offset or 0))
        if total_count > 0 and resolved_offset >= total_count:
            last_page_start = ((total_count - 1) // self.page_size) * self.page_size
            resolved_offset = max(0, int(last_page_start))
        customers = self.service.search_customers(
            keyword=keyword,
            limit=self.page_size,
            offset=resolved_offset,
        )
        return {
            "total_count": int(total_count or 0),
            "current_offset": int(resolved_offset),
            "customer_list": list(customers or []),
        }

    # edited by glg
    def _on_page_payload_ready(self, request_id, payload):
        if not self._is_latest_reload_request(request_id):
            return
        data = payload if isinstance(payload, dict) else {}
        self.total_count = int(data.get("total_count") or 0)
        self.current_offset = max(0, int(data.get("current_offset") or 0))
        self.customer_list = list(data.get("customer_list") or [])
        self.view.render_customers(self.customer_list, self.current_offset)
        self.view.update_pagination(
            current_offset=self.current_offset,
            total_count=self.total_count,
            page_size=self.page_size,
            current_count=len(self.customer_list),
        )

    def handle_cell_clicked(self, row, column):
        if column == 8:
            return
        self.select_row(row)

    def show_detail_dialog(self, row):
        if row < 0 or row >= len(self.customer_list):
            return
        customer = self.customer_list[row]
        self.view.show_customer_detail(customer)

    def select_row(self, row):
        item = self.view.table.item(row, 0)
        if item is not None:
            customer_id = item.data(Qt.UserRole)
            if customer_id is None:
                customer_id = item.text()
        else:
            customer_id = None
        self.view.selected_customer_id = (
            int(customer_id)
            if customer_id is not None and str(customer_id).isdigit()
            else None
        )
        self.view.accept()

    def key_pressed(self, event):
        if event.key() in (Qt.Key_Return, Qt.Key_Enter):
            current_row = self.view.table.currentRow()
            if current_row != -1 and not self.view.table.isRowHidden(current_row):
                self.select_row(current_row)
        elif event.key() == Qt.Key_Escape:
            self.view.reject()
