# edited by glg
from PySide6.QtWidgets import (
    QDialog,
    QCheckBox,
    QComboBox,
    QFormLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QMessageBox,
    QPlainTextEdit,
    QPushButton,
    QRadioButton,
    QSizePolicy,
    QVBoxLayout,
    QWidget,
)
from PySide6.QtPrintSupport import QPrinterInfo

from pypos.modules.printer.config.printer_config import PAPER_OPTIONS
from pypos.modules.printer.config.print_profile_config import (
    SETTLEMENT_LAYOUT_DEFAULTS,
)
from pypos.modules.auth.views.admin_verification_dialog import AdminVerificationDialog
from pypos.core.utils.ui_scale_runtime import scale_ui_px


class AddPrinterDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Tambah Printer")
        layout = QVBoxLayout(self)
        layout.addWidget(QLabel("Nama Printer"))
        self.name_combo = QComboBox()
        self.name_combo.setEditable(True)
        for info in QPrinterInfo.availablePrinters():
            self.name_combo.addItem(info.printerName())
        layout.addWidget(self.name_combo)

        layout.addWidget(QLabel("Ukuran Kertas"))
        self.paper_combo = QComboBox()
        self.paper_combo.addItems(list(PAPER_OPTIONS))
        layout.addWidget(self.paper_combo)

        self.default_check = QCheckBox("Jadikan default")
        layout.addWidget(self.default_check)

        btn_layout = QHBoxLayout()
        save_btn = QPushButton("Simpan")
        cancel_btn = QPushButton("Batal")
        save_btn.setProperty("class", "primary")
        cancel_btn.setProperty("class", "danger")
        save_btn.clicked.connect(self.accept)
        cancel_btn.clicked.connect(self.reject)
        btn_layout.addWidget(save_btn)
        btn_layout.addWidget(cancel_btn)
        layout.addLayout(btn_layout)

    def get_data(self):
        return {
            "name": self.name_combo.currentText(),
            "paper_size": self.paper_combo.currentText(),
            "default": self.default_check.isChecked(),
        }


class PrinterDiagnosticKasirDialog(QDialog):
    def __init__(self, title, content, parent=None):
        super().__init__(parent)
        self.setWindowTitle(str(title or "Diagnosa Printer"))
        # edited by glg
        # Hindari fixed geometry agar dialog bisa menyesuaikan resolusi/DPI.
        self.setMinimumSize(scale_ui_px(760), scale_ui_px(500))
        self.resize(scale_ui_px(860), scale_ui_px(560))
        if hasattr(self, "setSizeGripEnabled"):
            self.setSizeGripEnabled(True)

        layout = QVBoxLayout(self)
        intro = QLabel(
            "Hasil diagnosa printer ini sudah disederhanakan untuk kasir. "
            "Ikuti bagian 'Saran tindakan cepat' terlebih dahulu."
        )
        intro.setWordWrap(True)
        layout.addWidget(intro)

        output = QPlainTextEdit(self)
        output.setReadOnly(True)
        output.setPlainText(str(content or "Tidak ada data diagnosa printer."))
        layout.addWidget(output)

        button_layout = QHBoxLayout()
        button_layout.addStretch()
        close_btn = QPushButton("Tutup")
        close_btn.setProperty("class", "primary")
        close_btn.clicked.connect(self.accept)
        button_layout.addWidget(close_btn)
        layout.addLayout(button_layout)


class PrinterSettingsPanel(QWidget):
    def __init__(self, controller, parent=None):
        super().__init__(parent)
        self.controller = controller
        self._loading = False
        self._build_ui()
        self._load_printers()
        self._load_print_mode()
        self._load_settlement_print_options()

    def _build_ui(self):
        layout = QVBoxLayout(self)
        layout.setContentsMargins(8, 8, 8, 8)
        layout.setSpacing(10)

        usage_label = QLabel(
            "Setup printer untuk kasir:\n"
            "1. Pilih printer default dari daftar.\n"
            "2. Jika printer belum ada, klik Tambah.\n"
            "3. Klik Tes Print untuk memastikan struk keluar.\n"
            "4. Jika ada kendala, klik Diagnosa.\n"
            "5. Pilih mode cetak sesuai kebutuhan kasir."
        )
        usage_label.setWordWrap(True)
        usage_label.setStyleSheet(
            "background-color: #f5f8fc; border: 1px solid #dbe5f0; "
            "padding: 8px; border-radius: 4px;"
        )
        layout.addWidget(usage_label)

        printer_box = QGroupBox("Pengaturan Printer")
        printer_layout = QVBoxLayout()
        self.printer_combo = QComboBox()
        self.printer_combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        printer_layout.addWidget(QLabel("Printer Default:"))
        printer_layout.addWidget(self.printer_combo)

        self.default_info_label = QLabel("Default printer aktif: -")
        self.default_info_label.setStyleSheet("font-weight: bold; color: #2c3e50;")
        printer_layout.addWidget(self.default_info_label)

        btn_layout = QHBoxLayout()
        self.add_btn = QPushButton("Tambah")
        self.remove_btn = QPushButton("Hapus")
        self.diagnose_btn = QPushButton("Diagnosa")
        self.test_btn = QPushButton("Tes Print")
        self.preview_btn = QPushButton("Preview")
        # edited by glg
        # Pembungkus visual tombol agar aksi mudah dibedakan dari teks.
        self.add_btn.setProperty("class", "primary")
        self.remove_btn.setProperty("class", "danger")
        self.diagnose_btn.setProperty("class", "primary")
        self.test_btn.setProperty("class", "success")
        self.preview_btn.setProperty("class", "primary")
        btn_layout.addWidget(self.add_btn)
        btn_layout.addWidget(self.remove_btn)
        btn_layout.addStretch()
        btn_layout.addWidget(self.diagnose_btn)
        btn_layout.addWidget(self.test_btn)
        btn_layout.addWidget(self.preview_btn)
        printer_layout.addLayout(btn_layout)

        button_help_label = QLabel(
            "Keterangan tombol: "
            "Tambah untuk mendaftarkan printer baru, "
            "Hapus untuk menghapus printer yang dipilih, "
            "Tes Print untuk cetak uji, "
            "Preview untuk melihat contoh struk, "
            "Diagnosa untuk memeriksa masalah printer."
        )
        button_help_label.setWordWrap(True)
        button_help_label.setStyleSheet("color: #4b5563;")
        printer_layout.addWidget(button_help_label)
        printer_box.setLayout(printer_layout)
        layout.addWidget(printer_box)

        mode_box = QGroupBox("Mode Cetak Struk")
        mode_layout = QFormLayout()
        self.radio_preview = QRadioButton("Tampilkan Preview Dulu")
        self.radio_autoprint = QRadioButton("Langsung Auto Print")
        mode_layout.addRow(self.radio_preview)
        mode_layout.addRow(self.radio_autoprint)
        mode_help_label = QLabel(
            "Saran penggunaan: gunakan Preview saat awal setup printer. "
            "Jika hasil struk sudah benar dan stabil, gunakan Auto Print."
        )
        mode_help_label.setWordWrap(True)
        mode_help_label.setStyleSheet("color: #4b5563;")
        mode_layout.addRow(mode_help_label)
        mode_box.setLayout(mode_layout)
        layout.addWidget(mode_box)

        # edited by glg
        settlement_box = QGroupBox("Cetak Settlement")
        settlement_layout = QFormLayout()
        self.chk_settlement_cash_recap = QCheckBox("Tampilkan Rekap Kas")
        self.chk_settlement_transaksi_list = QCheckBox("Tampilkan Daftar Transaksi Settlement")
        lbl_cash_recap_help = QLabel(
            "Jika aktif, struk settlement menampilkan Total Harus, Total Disetor, dan Selisih "
            "untuk membantu audit kas harian."
        )
        lbl_cash_recap_help.setWordWrap(True)
        lbl_cash_recap_help.setStyleSheet("color: #4b5563;")
        lbl_transaksi_help = QLabel(
            "Jika aktif, struk settlement menampilkan daftar transaksi yang masuk ke batch settlement ini."
        )
        lbl_transaksi_help.setWordWrap(True)
        lbl_transaksi_help.setStyleSheet("color: #4b5563;")
        settlement_layout.addRow(self.chk_settlement_cash_recap)
        settlement_layout.addRow(lbl_cash_recap_help)
        settlement_layout.addRow(self.chk_settlement_transaksi_list)
        settlement_layout.addRow(lbl_transaksi_help)
        settlement_box.setLayout(settlement_layout)
        layout.addWidget(settlement_box)
        layout.addStretch(1)

        self.printer_combo.currentIndexChanged.connect(self._on_printer_selected)
        self.add_btn.clicked.connect(self._on_add_printer)
        self.remove_btn.clicked.connect(self._on_remove_printer)
        self.diagnose_btn.clicked.connect(self._on_diagnose_printer)
        self.test_btn.clicked.connect(self._on_test_print)
        self.preview_btn.clicked.connect(self._on_preview_print)
        self.radio_preview.toggled.connect(self._on_mode_changed)
        self.radio_autoprint.toggled.connect(self._on_mode_changed)
        self.chk_settlement_cash_recap.toggled.connect(self._on_settlement_option_changed)
        self.chk_settlement_transaksi_list.toggled.connect(self._on_settlement_option_changed)

    def _load_printers(self):
        self._loading = True
        self.printer_combo.blockSignals(True)
        try:
            self.printer_combo.clear()
            printers = self.controller.get_printers() or []
            default_index = 0
            for idx, printer in enumerate(printers):
                text = f"{printer.get('name', '-') } ({printer.get('paper_size', '-')})"
                self.printer_combo.addItem(text, idx)
                if printer.get("default"):
                    default_index = idx
            if printers:
                self.printer_combo.setCurrentIndex(default_index)
            default_printer = self.controller.get_default_printer() or {}
            if default_printer.get("name"):
                self.default_info_label.setText(f"Default printer aktif: {default_printer.get('name')}")
            else:
                self.default_info_label.setText("Default printer aktif: -")
        finally:
            self.printer_combo.blockSignals(False)
            self._loading = False

    def _load_print_mode(self):
        self._loading = True
        try:
            mode = str(self.controller.get_print_mode() or "preview").lower()
            if mode == "auto":
                self.radio_autoprint.setChecked(True)
            else:
                self.radio_preview.setChecked(True)
        finally:
            self._loading = False

    # edited by glg
    def _load_settlement_print_options(self):
        self._loading = True
        try:
            cash_recap_default = bool(SETTLEMENT_LAYOUT_DEFAULTS.get("show_cash_recap", True))
            transaksi_default = bool(SETTLEMENT_LAYOUT_DEFAULTS.get("show_transaction_list", False))
            # edited by glg
            # Backward-compatibility:
            # controller lama (test stub/instalasi lama) mungkin belum punya API option settlement.
            if hasattr(self.controller, "get_settlement_print_option"):
                show_cash_recap = bool(
                    self.controller.get_settlement_print_option(
                        "show_cash_recap",
                        default=cash_recap_default,
                    )
                )
                show_transaksi = bool(
                    self.controller.get_settlement_print_option(
                        "show_transaction_list",
                        default=transaksi_default,
                    )
                )
            else:
                show_cash_recap = cash_recap_default
                show_transaksi = transaksi_default
            self.chk_settlement_cash_recap.setChecked(show_cash_recap)
            self.chk_settlement_transaksi_list.setChecked(show_transaksi)
        finally:
            self._loading = False

    def _on_printer_selected(self, index):
        if self._loading or index < 0:
            return
        self.controller.set_default_printer(index)
        self._load_printers()

    def _on_add_printer(self):
        dialog = AddPrinterDialog(self)
        if dialog.exec() != QDialog.Accepted:
            return
        payload = dialog.get_data()
        name = str(payload.get("name") or "").strip()
        paper_size = str(payload.get("paper_size") or "").strip()
        is_default = bool(payload.get("default"))
        if not name or not paper_size:
            QMessageBox.warning(self, "Data tidak lengkap", "Nama printer dan ukuran kertas wajib diisi.")
            return
        self.controller.add_printer(name, paper_size, is_default)
        self._load_printers()

    def _on_remove_printer(self):
        index = self.printer_combo.currentIndex()
        if index < 0:
            return
        self.controller.remove_printer(index)
        self._load_printers()

    def _on_test_print(self):
        index = self.printer_combo.currentIndex()
        if index < 0:
            return
        result = self.controller.test_print(index)
        if result:
            QMessageBox.information(self, "Tes Print", "Tes print berhasil.")
        else:
            QMessageBox.warning(self, "Tes Print", "Tes print gagal.")

    def _on_preview_print(self):
        index = self.printer_combo.currentIndex()
        if index < 0:
            return
        self.controller.preview_print(index, self)

    def _on_diagnose_printer(self):
        try:
            result = self.controller.run_printer_diagnostic() or {}
            report = result.get("report") or {}
            report_path = result.get("report_path") or "-"
            text = self._build_kasir_diagnostic_text(report, report_path)
            dlg = PrinterDiagnosticKasirDialog("Diagnosa Printer", text, self)
            dlg.exec()
        except (AttributeError, RuntimeError, TypeError, ValueError) as exc:
            QMessageBox.warning(self, "Diagnosa Printer", f"Gagal menjalankan diagnosa printer.\n{exc}")

    def _translate_diagnostic_message(self, message):
        text = str(message or "").strip()
        lower = text.lower()

        if "tidak ada printer terdaftar" in lower:
            return "Belum ada printer di aplikasi. Klik Tambah lalu pilih printer."
        if "default printer tidak valid" in lower or "harus 1" in lower:
            return "Default printer belum benar. Pastikan hanya satu printer yang dijadikan default."
        if "default printer tunggal valid" in lower:
            return "Default printer sudah benar."
        if "printer os terdeteksi" in lower:
            try:
                count = int(text.split(":")[-1].strip())
            except (TypeError, ValueError):
                count = -1
            if count <= 0:
                return "Windows belum menemukan printer terpasang."
            return f"Windows menemukan {count} printer."
        if "default printer os" in lower:
            val = text.split(":", 1)[-1].strip()
            if not val or val == "-":
                return "Windows belum punya default printer."
            return f"Default printer Windows adalah {val}."
        if "nama printer tidak ditemukan di os" in lower:
            return "Nama printer di aplikasi tidak cocok dengan nama printer di Windows."
        if "nama printer ditemukan di os" in lower:
            return "Nama printer cocok dengan yang ada di Windows."
        if "koneksi lan berhasil" in lower:
            return "Koneksi jaringan ke printer LAN berhasil."
        if "koneksi lan gagal" in lower:
            return "Tidak bisa terhubung ke printer LAN. Cek kabel LAN, IP printer, dan jaringan."
        if "format address lan tidak valid" in lower:
            return "Alamat printer LAN salah. Gunakan format IP:PORT, contoh 192.168.1.10:9100."
        if "format address usb tidak valid" in lower:
            return "Alamat printer USB salah. Hubungi IT untuk perbaiki format VID:PID."
        if "device usb tidak ditemukan" in lower:
            return "Printer USB belum terdeteksi. Cek kabel USB dan nyalakan printer."
        if "device usb terdeteksi" in lower:
            return "Printer USB berhasil terdeteksi."
        if "backend usb tidak tersedia" in lower or "libusb" in lower:
            return "Driver USB printer belum siap. Hubungi IT untuk pemasangan driver."
        if "module escpos belum tersedia" in lower:
            return "Komponen cetak belum lengkap. Hubungi IT."
        if "module escpos terdeteksi" in lower:
            return "Komponen inti printer siap."
        if "module usb (pyusb) wajib dipasang" in lower:
            return "Komponen USB wajib dipasang karena outlet memakai printer USB. Hubungi IT."
        if "module usb (pyusb) belum tersedia" in lower:
            return "Komponen USB belum tersedia. Jika pakai USB, hubungi IT."
        if "module usb (pyusb) terdeteksi" in lower:
            return "Komponen USB siap."
        if "module serial (pyserial) belum tersedia" in lower:
            return "Komponen serial belum tersedia (opsional)."
        if "module serial (pyserial) terdeteksi" in lower:
            return "Komponen serial siap."
        if "win32print tidak tersedia" in lower:
            return "Pemeriksaan spooler Windows tidak tersedia (opsional)."
        if "win32print tersedia" in lower:
            return "Pemeriksaan spooler Windows tersedia."
        if "printer terdaftar:" in lower:
            try:
                count = int(text.split(":")[-1].strip())
            except (TypeError, ValueError):
                count = -1
            if count <= 0:
                return "Belum ada printer yang terdaftar."
            return f"Printer terdaftar di aplikasi: {count}."
        if "file db ditemukan" in lower:
            return "File database aplikasi ditemukan."
        if "file db tidak ditemukan" in lower:
            return "File database aplikasi tidak ditemukan. Hubungi IT."
        if "gagal membaca konfigurasi printer" in lower:
            return "Aplikasi gagal membaca data printer. Hubungi IT."
        if "gagal baca printer os" in lower:
            return "Aplikasi gagal membaca daftar printer dari Windows."
        if "mode spooler os" in lower:
            return "Printer menggunakan mode bawaan Windows."
        return text

    def _translate_recommendation(self, rec):
        text = str(rec or "").strip()
        lower = text.lower()
        if "libusb" in lower:
            return "Pasang driver USB printer (dibantu IT) agar printer USB bisa dipakai."
        if "vid:pid" in lower:
            return "Perbaiki data printer USB dengan bantuan IT."
        if "ip:port" in lower:
            return "Perbaiki alamat printer LAN ke format IP:PORT."
        if "default" in lower and "printer" in lower:
            return "Pastikan hanya satu printer yang dijadikan default."
        if "nama printer" in lower and "windows" in lower:
            return "Samakan nama printer di aplikasi dengan nama printer di Windows."
        if "tambahkan minimal satu printer" in lower:
            return "Tambahkan minimal satu printer sebelum operasional kasir."
        return text

    def _diagnostic_scope_label(self, category, printer_name):
        if printer_name:
            return f"Printer '{printer_name}'"
        cat = str(category or "").strip().lower()
        if cat == "paths":
            return "File aplikasi"
        if cat == "dependency":
            return "Komponen aplikasi"
        if cat == "configuration":
            return "Pengaturan printer"
        if cat == "runtime":
            return "Koneksi ke Windows"
        return "Sistem"

    def _append_rows_by_status(self, lines, rows, status_code, title):
        lines.append(title)
        idx = 1
        for row in rows:
            status = str(row.get("status") or "").upper()
            if status != status_code:
                continue
            scope = str(row.get("scope") or "Sistem")
            message = str(row.get("message") or "-")
            lines.append(f"{idx}. {scope}: {message}")
            idx += 1
        if idx == 1:
            lines.append("- Tidak ada.")
        lines.append("")

    def _build_kasir_diagnostic_text(self, report, report_path):
        payload = report if isinstance(report, dict) else {}
        summary = payload.get("summary") if isinstance(payload.get("summary"), dict) else {}
        pass_count = int(summary.get("PASS", 0) or 0)
        warn_count = int(summary.get("WARN", 0) or 0)
        fail_count = int(summary.get("FAIL", 0) or 0)

        status_umum = "Printer siap dipakai."
        if fail_count > 0:
            status_umum = "Ada masalah penting. Cetak bisa gagal."
        elif warn_count > 0:
            status_umum = "Perlu perhatian. Printer mungkin masih bisa dipakai."

        rows = []
        checks = payload.get("checks") if isinstance(payload.get("checks"), list) else []
        for item in checks:
            if not isinstance(item, dict):
                continue
            rows.append(
                {
                    "status": str(item.get("status") or "").upper(),
                    "scope": self._diagnostic_scope_label(item.get("category"), ""),
                    "message": self._translate_diagnostic_message(item.get("message")),
                }
            )

        printers = payload.get("printers") if isinstance(payload.get("printers"), list) else []
        for printer in printers:
            if not isinstance(printer, dict):
                continue
            printer_name = str(printer.get("name") or "").strip()
            printer_checks = printer.get("checks") if isinstance(printer.get("checks"), list) else []
            for item in printer_checks:
                if not isinstance(item, dict):
                    continue
                rows.append(
                    {
                        "status": str(item.get("status") or "").upper(),
                        "scope": self._diagnostic_scope_label("", printer_name or "-"),
                        "message": self._translate_diagnostic_message(item.get("message")),
                    }
                )

        recommendations = payload.get("recommendations") if isinstance(payload.get("recommendations"), list) else []
        lines = [
            f"Status umum: {status_umum}",
            f"Masalah penting: {fail_count}",
            f"Peringatan: {warn_count}",
            f"Pengecekan normal: {pass_count}",
            "",
            "Saran tindakan cepat:",
        ]
        if recommendations:
            for idx, rec in enumerate(recommendations, start=1):
                lines.append(f"{idx}. {self._translate_recommendation(rec)}")
        else:
            lines.append("1. Lanjutkan tes print untuk memastikan struk keluar dengan benar.")
        lines.append("")
        lines.append("LOG HASIL DIAGNOSA PRINTER:")
        lines.append("")

        self._append_rows_by_status(lines, rows, "FAIL", "Masalah penting yang harus diperbaiki:")
        self._append_rows_by_status(lines, rows, "WARN", "Hal yang perlu dicek:")
        self._append_rows_by_status(lines, rows, "PASS", "Hal yang sudah oke:")
        self._append_rows_by_status(lines, rows, "INFO", "Catatan tambahan:")

        lines.append("Catatan teknis file diagnosa:")
        lines.append(str(report_path or "-"))
        return "\n".join(lines)

    def _on_mode_changed(self):
        if self._loading:
            return
        mode = "auto" if self.radio_autoprint.isChecked() else "preview"
        self.controller.set_print_mode(mode)

    # edited by glg
    def _on_settlement_option_changed(self):
        if self._loading:
            return
        sender = self.sender()
        option_key = ""
        enabled = False
        if sender is self.chk_settlement_cash_recap:
            option_key = "show_cash_recap"
            enabled = bool(self.chk_settlement_cash_recap.isChecked())
        elif sender is self.chk_settlement_transaksi_list:
            option_key = "show_transaction_list"
            enabled = bool(self.chk_settlement_transaksi_list.isChecked())
        else:
            self._load_settlement_print_options()
            return

        auth_dialog = AdminVerificationDialog(self)
        # edited by glg
        # Wajib verifikasi admin settlement untuk setiap perubahan opsi cetak settlement.
        if auth_dialog.exec() != QDialog.Accepted:
            self._load_settlement_print_options()
            return
        admin_username, admin_password = auth_dialog.get_credentials()

        ok, message, _payload = self.controller.update_settlement_print_option(
            option_key,
            enabled,
            admin_username,
            admin_password,
        )
        if not ok:
            QMessageBox.warning(self, "Otorisasi", message or "Gagal memperbarui pengaturan cetak settlement.")
            self._load_settlement_print_options()
            return
