# edited by glg
import time

from pypos.modules.scanner.services.rawinput_scanner_service import RawInputScannerService
from pypos.modules.scanner.services.scanner_detector_service import ScannerDetectorService
from pypos.modules.scanner.services.scanner_event_guard_service import ScannerEventGuardService


class ScannerRuntimeService:
    _LAST_SCAN_RECENT_TTL_MS = 15000
    _SCANNER_EVENT_SOURCES = {"wedge_key", "wedge_poll", "rawinput_poll"}

    def __init__(self, settings, log_warning=None, log_debug=None):
        self._settings = settings if isinstance(settings, dict) else {}
        self._log_warning = log_warning or (lambda *_: None)
        self._log_debug = log_debug or (lambda *_: None)
        self._detector = ScannerDetectorService(self._settings)
        self._rawinput = RawInputScannerService(
            settings=self._settings,
            log_warning=self._log_warning,
            log_debug=self._log_debug,
        )
        self._guard = ScannerEventGuardService(
            dedup_window_ms=int(self._settings.get("duplicate_guard_ms", 180) or 180)
        )
        self._started = False
        self._last_scan_ms = 0
        self._last_scan_source = ""

    def _current_ms(self):
        return int(time.monotonic() * 1000)

    def _last_scan_recent(self, now_ms):
        if int(self._last_scan_ms or 0) <= 0:
            return False
        return (int(now_ms) - int(self._last_scan_ms)) <= int(self._LAST_SCAN_RECENT_TTL_MS)

    def start(self):
        if self._started:
            return
        self._rawinput.start()
        self._started = True

    def stop(self):
        self._rawinput.stop()
        self._started = False

    def apply_settings(self, settings):
        self._settings = settings if isinstance(settings, dict) else {}
        self._detector.apply_settings(self._settings)
        self._rawinput.apply_settings(self._settings)
        self._guard.apply_window(int(self._settings.get("duplicate_guard_ms", 180) or 180))

    def get_runtime_status(self):
        raw_payload = {}
        try:
            raw_payload = self._rawinput.get_status()
        except (TypeError, ValueError, KeyError, AttributeError, RuntimeError, OSError, LookupError, ArithmeticError, ImportError) as e:
            raw_payload = {"active": False, "capture_enabled": False, "last_error": str(e)}
        now_ms = self._current_ms()
        last_scan_ms = int(self._last_scan_ms or 0)
        last_scan_age_ms = -1
        if last_scan_ms > 0:
            last_scan_age_ms = max(0, int(now_ms) - last_scan_ms)
        return {
            "rawinput": raw_payload,
            "wedge": {"enabled": bool(self._detector.is_enabled())},
            "settings": dict(self._settings or {}),
            "scanner_activity": {
                "last_scan_ms": last_scan_ms,
                "last_scan_source": str(self._last_scan_source or ""),
                "last_scan_recent": bool(self._last_scan_recent(now_ms)),
                "last_scan_age_ms": int(last_scan_age_ms),
                "last_scan_recent_ttl_ms": int(self._LAST_SCAN_RECENT_TTL_MS),
            },
        }

    def _accept(self, barcode, source):
        value = str(barcode or "").strip()
        if not value:
            return None
        if not self._guard.should_accept(value, source):
            return None
        src = str(source or "").strip().lower()
        if src in self._SCANNER_EVENT_SOURCES:
            self._last_scan_ms = self._current_ms()
            self._last_scan_source = src
        return value

    def consume_qt_key_event(self, event):
        if self._rawinput.is_capture_enabled():
            return None
        if not self._detector.is_enabled():
            return None
        consumed = self._detector.consume_qt_key_event(event)
        if not isinstance(consumed, dict):
            return None
        if not consumed.get("handled"):
            return None
        return self._accept(consumed.get("barcode"), "wedge_key")

    def poll_pending_barcode(self):
        if self._rawinput.is_capture_enabled():
            barcode = self._rawinput.poll_pending_barcode()
            return self._accept(barcode, "rawinput_poll")
        if not self._detector.is_enabled():
            return None
        barcode = self._detector.poll_pending_barcode()
        return self._accept(barcode, "wedge_poll")

    def accept_manual_barcode(self, barcode):
        return self._accept(barcode, "manual_barcode")

    def get_discovered_devices(self):
        payload = self.get_runtime_status()
        raw = payload.get("rawinput") if isinstance(payload.get("rawinput"), dict) else {}
        values = raw.get("discovered_devices") if isinstance(raw.get("discovered_devices"), list) else []
        return values
