# edited by glg
import time

from PySide6.QtCore import QTimer, Signal
from PySide6.QtWidgets import (
    QComboBox,
    QFormLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QMessageBox,
    QPushButton,
    QVBoxLayout,
    QWidget,
    QCheckBox,
)

from pypos.core.utils.scanner_device_utils import extract_device_match_token
from pypos.modules.scanner.config.scanner_profile_config import (
    build_scanner_easy_payload,
    get_scanner_advanced_profile,
)


class ScannerSettingsView(QWidget):
    settings_saved = Signal(dict)

    _DETECT_POLL_MS = 400
    _DETECT_TIMEOUT_MS = 8000

    def __init__(self, controller, parent=None):
        super().__init__(parent)
        self.controller = controller
        self._whitelist_tokens = []
        self._detect_started_at = 0.0
        self._detect_timer = QTimer(self)
        self._detect_timer.setInterval(self._DETECT_POLL_MS)
        self._detect_timer.timeout.connect(self._on_detect_timer_tick)
        self._build_ui()
        self._load_settings()

    def _build_ui(self):
        wrapper_layout = QVBoxLayout(self)
        wrapper_layout.setContentsMargins(10, 10, 10, 10)
        wrapper_layout.setSpacing(10)

        info_awal = QLabel(
            "Setup scanner untuk kasir:\n"
            "1. Aktifkan scanner.\n"
            "2. Klik Deteksi Scanner.\n"
            "3. Scan barcode sekali.\n"
            "4. Pilih device lalu klik Gunakan Device Ini."
        )
        info_awal.setWordWrap(True)
        wrapper_layout.addWidget(info_awal)

        box = QGroupBox("Setup Scanner (Mudah)")
        form = QFormLayout()
        form.setSpacing(8)

        self.enabled_checkbox = QCheckBox("Aktifkan scanner barcode untuk outlet ini")
        form.addRow(QLabel("Status Scanner"), self.enabled_checkbox)

        detect_buttons = QHBoxLayout()
        self.detect_button = QPushButton("Deteksi Scanner")
        self.refresh_device_button = QPushButton("Muat Ulang Daftar")
        self.detect_button.setProperty("class", "primary")
        self.refresh_device_button.setProperty("class", "primary")
        detect_buttons.addWidget(self.detect_button)
        detect_buttons.addWidget(self.refresh_device_button)
        detect_buttons.addStretch()
        detect_widget = QWidget()
        detect_widget.setLayout(detect_buttons)
        form.addRow(QLabel("Deteksi Device"), detect_widget)

        self.discovered_device_combo = QComboBox()
        self.discovered_device_combo.addItem("Belum ada scanner terdeteksi", None)
        form.addRow(QLabel("Scanner Terdeteksi"), self.discovered_device_combo)

        pairing_buttons = QHBoxLayout()
        self.pair_replace_button = QPushButton("Gunakan Device Ini")
        self.pair_append_button = QPushButton("Tambah Device Cadangan")
        self.pair_replace_button.setProperty("class", "success")
        self.pair_append_button.setProperty("class", "primary")
        pairing_buttons.addWidget(self.pair_replace_button)
        pairing_buttons.addWidget(self.pair_append_button)
        pairing_widget = QWidget()
        pairing_widget.setLayout(pairing_buttons)
        form.addRow(QLabel("Simpan Pilihan"), pairing_widget)

        self.pairing_status_label = QLabel("-")
        self.pairing_status_label.setWordWrap(True)
        form.addRow(QLabel("Status Outlet"), self.pairing_status_label)

        box.setLayout(form)
        wrapper_layout.addWidget(box)

        button_layout = QHBoxLayout()
        button_layout.addStretch()
        self.check_button = QPushButton("Cek Scanner")
        self.save_button = QPushButton("Simpan")
        self.reset_button = QPushButton("Muat Ulang")
        self.check_button.setProperty("class", "primary")
        self.reset_button.setProperty("class", "primary")
        self.save_button.setProperty("class", "success")
        button_layout.addWidget(self.check_button)
        button_layout.addWidget(self.reset_button)
        button_layout.addWidget(self.save_button)
        wrapper_layout.addLayout(button_layout)
        wrapper_layout.addStretch(1)

        self.detect_button.clicked.connect(self._start_detect_session)
        self.refresh_device_button.clicked.connect(self._refresh_discovered_devices)
        self.pair_append_button.clicked.connect(self._pair_selected_device_append)
        self.pair_replace_button.clicked.connect(self._pair_selected_device_replace)
        self.discovered_device_combo.currentIndexChanged.connect(self._update_pairing_status)
        self.enabled_checkbox.toggled.connect(self._update_pairing_status)
        self.check_button.clicked.connect(self._run_health_check)
        self.save_button.clicked.connect(self._save_settings)
        self.reset_button.clicked.connect(self._load_settings)

    def _normalize_whitelist(self, raw):
        values = raw if isinstance(raw, list) else []
        cleaned = []
        seen = set()
        for item in values:
            token = str(item or "").strip().lower()
            if not token or token in seen:
                continue
            cleaned.append(token)
            seen.add(token)
        return cleaned

    def _load_settings(self):
        self._stop_detect_session()
        cfg = self.controller.load_scanner_settings() or {}
        self.enabled_checkbox.setChecked(bool(cfg.get("enabled")))
        self._whitelist_tokens = self._normalize_whitelist(cfg.get("rawinput_whitelist"))
        self._refresh_discovered_devices()
        self._update_pairing_status()

    def _build_payload(self):
        return build_scanner_easy_payload(
            enabled=bool(self.enabled_checkbox.isChecked()),
            rawinput_whitelist=list(self._whitelist_tokens),
        )

    def _save_settings(self):
        saved = self.controller.save_scanner_settings(self._build_payload()) or {}
        self._whitelist_tokens = self._normalize_whitelist(saved.get("rawinput_whitelist"))
        self.settings_saved.emit(saved)
        self._refresh_discovered_devices()
        self._update_pairing_status()

    def _selected_discovered_device(self):
        data = self.discovered_device_combo.currentData()
        if not isinstance(data, dict):
            return None
        name = str(data.get("name") or "").strip()
        token = str(data.get("token") or "").strip().lower()
        if not name:
            return None
        return {"name": name, "token": token}

    def _refresh_discovered_devices(self, devices=None):
        rows = devices if isinstance(devices, list) else (self.controller.get_scanner_discovered_devices() or [])
        current = self._selected_discovered_device()
        current_key = ""
        if isinstance(current, dict):
            current_key = f"{current.get('name')}::{current.get('token')}"

        self.discovered_device_combo.blockSignals(True)
        self.discovered_device_combo.clear()
        if not rows:
            self.discovered_device_combo.addItem(
                "Belum ada scanner terdeteksi. Klik Deteksi Scanner lalu scan barcode sekali.",
                None,
            )
        else:
            selected_index = 0
            for idx, item in enumerate(rows):
                name = str(item.get("name") or "").strip()
                token = str(item.get("token") or "").strip().lower()
                payload = {"name": name, "token": token}
                self.discovered_device_combo.addItem(name, payload)
                key = f"{name}::{token}"
                if current_key and key == current_key:
                    selected_index = idx
            self.discovered_device_combo.setCurrentIndex(selected_index)
        self.discovered_device_combo.blockSignals(False)
        self._update_pairing_status()

    def _pair_device(self, replace):
        selected = self._selected_discovered_device()
        if not selected:
            self.pairing_status_label.setText(
                "Belum ada scanner terpilih. Klik Deteksi Scanner dulu lalu pilih dari daftar."
            )
            return
        saved = self.controller.pair_scanner_device(selected_device=selected, replace=replace) or {}
        if not saved:
            self.pairing_status_label.setText(
                "Penyimpanan scanner gagal. Coba deteksi ulang lalu scan barcode sekali lagi."
            )
            return
        self._whitelist_tokens = self._normalize_whitelist(saved.get("rawinput_whitelist"))
        self.settings_saved.emit(saved)
        self._update_pairing_status()

    def _pair_selected_device_append(self):
        self._pair_device(replace=False)

    def _pair_selected_device_replace(self):
        self._pair_device(replace=True)

    def _is_config_ready(self):
        profile = get_scanner_advanced_profile()
        backend_mode = str(profile.get("backend_mode") or "").strip().lower()
        rawinput_enabled = bool(profile.get("rawinput_enabled"))
        return bool(backend_mode in {"auto", "rawinput"} and rawinput_enabled)

    def _count_real_discovered_devices(self):
        count = 0
        for idx in range(self.discovered_device_combo.count()):
            if isinstance(self.discovered_device_combo.itemData(idx), dict):
                count += 1
        return count

    def _selected_device_token(self):
        selected = self._selected_discovered_device()
        if not selected:
            return ""
        token = str(selected.get("token") or "").strip().lower()
        if token:
            return token
        name = str(selected.get("name") or "").strip()
        return extract_device_match_token(name)

    def _update_pairing_status(self):
        enabled = bool(self.enabled_checkbox.isChecked())
        config_ready = bool(self._is_config_ready())
        selected = self._selected_discovered_device()
        selected_name = str((selected or {}).get("name") or "-")
        selected_token = self._selected_device_token()
        discovered_count = self._count_real_discovered_devices()
        is_paired = bool(selected_token and selected_token in self._whitelist_tokens)
        status = "Siap dipakai" if is_paired and enabled and config_ready else "Belum siap"

        langkah_berikut = "Scanner siap dipakai untuk transaksi."
        if not config_ready:
            langkah_berikut = (
                "Konfigurasi lanjutan scanner tidak valid. Hubungi IT untuk cek file config scanner."
            )
        elif not enabled:
            langkah_berikut = "Aktifkan scanner terlebih dahulu."
        elif discovered_count <= 0:
            langkah_berikut = "Klik Deteksi Scanner lalu scan barcode sekali."
        elif not selected:
            langkah_berikut = "Pilih scanner dari daftar."
        elif not is_paired:
            langkah_berikut = "Klik Gunakan Device Ini agar tersimpan."

        self.pairing_status_label.setText(
            "Status scanner outlet ini: "
            f"<b>{status}</b><br>"
            f"Scanner terdeteksi: <b>{discovered_count}</b><br>"
            f"Scanner dipilih: <b>{selected_name}</b><br>"
            f"Langkah berikutnya: <b>{langkah_berikut}</b>"
        )

    def _run_health_check(self):
        report = self.controller.run_scanner_diagnostic() or {}
        overall = str(report.get("overall_status") or "-").upper()
        enabled = bool(self.enabled_checkbox.isChecked())
        config_ready = bool(self._is_config_ready())
        selected_token = self._selected_device_token()
        is_paired = bool(selected_token and selected_token in self._whitelist_tokens)
        discovered = self._count_real_discovered_devices()

        lines = [f"Hasil cek scanner: {overall}"]
        if not config_ready:
            lines.append("Konfigurasi lanjutan scanner tidak valid. Hubungi IT.")
        if not enabled:
            lines.append("Scanner masih nonaktif.")
        if discovered <= 0:
            lines.append("Belum ada scanner terdeteksi. Jalankan Deteksi Scanner dan scan sekali.")
        if discovered > 0 and not is_paired:
            lines.append("Scanner sudah terdeteksi, tetapi belum disimpan. Klik Gunakan Device Ini.")
        if enabled and config_ready and is_paired:
            lines.append("Scanner sudah siap digunakan di transaksi penjualan.")

        message = "\n".join(lines)
        if enabled and config_ready and is_paired and overall != "FAIL":
            QMessageBox.information(self, "Cek Scanner", message)
            return
        QMessageBox.warning(self, "Cek Scanner", message)

    def _prepare_detection_session(self):
        if not self._is_config_ready():
            QMessageBox.warning(
                self,
                "Deteksi Scanner",
                "Konfigurasi lanjutan scanner tidak valid.\n"
                "Silakan hubungi IT untuk pengecekan file config scanner.",
            )
            return False
        if not self.enabled_checkbox.isChecked():
            self.enabled_checkbox.setChecked(True)
        try:
            saved = self.controller.save_scanner_settings(self._build_payload()) or {}
            self._whitelist_tokens = self._normalize_whitelist(saved.get("rawinput_whitelist"))
            self.settings_saved.emit(saved)
            return True
        except (TypeError, ValueError, KeyError, AttributeError, RuntimeError, OSError, LookupError, ArithmeticError, ImportError) as exc:
            QMessageBox.warning(
                self,
                "Deteksi Scanner",
                f"Gagal menyiapkan deteksi scanner.\n{exc}",
            )
            return False

    def _start_detect_session(self):
        if not self._prepare_detection_session():
            return
        self._refresh_discovered_devices()
        self._detect_started_at = time.monotonic()
        self.detect_button.setEnabled(False)
        self.detect_button.setText("Mendeteksi...")
        self.pairing_status_label.setText(
            "Sedang mendeteksi scanner. Silakan scan barcode sekali sekarang."
        )
        self._detect_timer.start()

    def _stop_detect_session(self):
        if self._detect_timer.isActive():
            self._detect_timer.stop()
        self.detect_button.setEnabled(True)
        self.detect_button.setText("Deteksi Scanner")

    def _on_detect_timer_tick(self):
        self._refresh_discovered_devices()
        total_device = self._count_real_discovered_devices()
        elapsed_ms = int((time.monotonic() - float(self._detect_started_at or 0.0)) * 1000)

        if total_device > 0:
            self._stop_detect_session()
            self._update_pairing_status()
            QMessageBox.information(
                self,
                "Deteksi Scanner",
                "Scanner terdeteksi. Pilih scanner dari daftar lalu klik Gunakan Device Ini.",
            )
            return

        if elapsed_ms >= int(self._DETECT_TIMEOUT_MS):
            self._stop_detect_session()
            self._update_pairing_status()
            QMessageBox.warning(
                self,
                "Deteksi Scanner",
                "Scanner belum terdeteksi.\n"
                "1. Pastikan scanner terhubung.\n"
                "2. Klik Deteksi Scanner lagi.\n"
                "3. Saat proses berjalan, scan barcode sekali.",
            )
            return

        remaining = max(1, int((self._DETECT_TIMEOUT_MS - elapsed_ms + 999) / 1000))
        self.pairing_status_label.setText(
            "Sedang mendeteksi scanner. Silakan scan barcode sekali sekarang. "
            f"Sisa waktu: <b>{remaining}</b> detik."
        )
