"""Qt 6 (PyQt6) implementation of the Edubuntu Menu Administration UI."""

from __future__ import annotations

import threading
from collections.abc import Callable

from PyQt6.QtCore import Qt, QTimer
from PyQt6.QtGui import QFontMetrics, QIcon
from PyQt6.QtWidgets import (
    QApplication,
    QCheckBox,
    QComboBox,
    QDialog,
    QDialogButtonBox,
    QFrame,
    QHBoxLayout,
    QLabel,
    QMessageBox,
    QProgressDialog,
    QScrollArea,
    QTabWidget,
    QVBoxLayout,
    QWidget,
)

from .base import MenuAdminUI, ChecklistItem
from i18n import _

ICON_PATH = "/usr/share/icons/hicolor/scalable/apps/edubuntu-menu-admin.svg"

_MAX_DLG_WIDTH = 950


def _item_text(label: str, description: str) -> str:
    if description:
        return "{0}  \u2014  {1}".format(label, description)
    return label


def _msgbox(icon: QMessageBox.Icon, title: str, text: str) -> QMessageBox:
    box = QMessageBox(icon, title, text)
    box.setWindowIcon(QIcon(ICON_PATH))
    for lbl in box.findChildren(QLabel):
        lbl.setWordWrap(True)
        lbl.setMaximumWidth(_MAX_DLG_WIDTH - 80)
    return box


def _wrapping_row(button: QCheckBox, text: str) -> QWidget:
    label = QLabel(text)
    label.setWordWrap(True)
    label.mousePressEvent = lambda _ev: button.toggle()
    row = QWidget()
    h = QHBoxLayout(row)
    h.setContentsMargins(4, 3, 4, 3)
    h.addWidget(button)
    h.addWidget(label, 1)
    return row


def _auto_size(dlg: QDialog, width: int = 0, height: int = 0) -> None:
    if width > 0:
        screen = QApplication.primaryScreen()
        max_h = int(screen.availableGeometry().height() * 0.8) if screen else 800
        dlg.resize(width, 0)
        dlg.adjustSize()
        h = height if height > 0 else min(dlg.sizeHint().height(), max_h)
        dlg.resize(width, h)
        return

    dlg.adjustSize()
    hint = dlg.sizeHint()
    w = min(hint.width(), _MAX_DLG_WIDTH)
    screen = QApplication.primaryScreen()
    max_h = int(screen.availableGeometry().height() * 0.8) if screen else 800
    h = min(hint.height(), max_h)
    dlg.resize(w, h)


def _items_width(items: list[ChecklistItem]) -> int:
    fm = QFontMetrics(QApplication.font())
    widest = max(
        fm.horizontalAdvance(_item_text(it.label, it.description))
        for it in items
    )
    return min(widest + 60, _MAX_DLG_WIDTH)


def _build_checklist_scroll(
    items: list[ChecklistItem],
) -> tuple[QScrollArea, list[tuple[str, QCheckBox]]]:
    scroll = QScrollArea()
    scroll.setWidgetResizable(True)
    scroll.setFrameShape(QFrame.Shape.NoFrame)
    container = QWidget()
    vbox = QVBoxLayout(container)
    vbox.setSpacing(2)
    vbox.setContentsMargins(4, 4, 4, 4)

    checks: list[tuple[str, QCheckBox]] = []
    for item in items:
        cb = QCheckBox()
        cb.setChecked(item.checked)
        row = _wrapping_row(cb, _item_text(item.label, item.description))
        if item.disabled:
            cb.setEnabled(False)
            row.setEnabled(False)
        vbox.addWidget(row)
        checks.append((item.key, cb))

    scroll.setWidget(container)
    return scroll, checks


class QtUI(MenuAdminUI):

    def __init__(self) -> None:
        self._app: QApplication | None = None

    def init(self) -> None:
        if QApplication.instance() is None:
            self._app = QApplication([])
            self._app.setApplicationName(_("Edubuntu Menu Administration"))
            self._app.setWindowIcon(QIcon(ICON_PATH))
        else:
            self._app = QApplication.instance()

    def quit(self) -> None:
        pass

    def show_info(self, title: str, text: str) -> None:
        box = _msgbox(QMessageBox.Icon.Information, title, text)
        box.exec()

    def show_error(self, title: str, text: str) -> None:
        box = _msgbox(QMessageBox.Icon.Critical, title, text)
        box.exec()

    def show_question(
        self,
        title: str,
        text: str,
        ok_label: str = "Yes",
        cancel_label: str = "No",
    ) -> bool:
        box = _msgbox(QMessageBox.Icon.Question, title, text)
        btn_ok = box.addButton(ok_label, QMessageBox.ButtonRole.AcceptRole)
        box.addButton(cancel_label, QMessageBox.ButtonRole.RejectRole)
        box.exec()
        return box.clickedButton() == btn_ok

    def show_checklist(
        self,
        title: str,
        text: str,
        items: list[ChecklistItem],
        ok_label: str = "OK",
        cancel_label: str = "Cancel",
        width: int = 0,
        height: int = 0,
    ) -> list[str] | None:
        dlg = QDialog()
        dlg.setWindowTitle(title)
        dlg.setWindowIcon(QIcon(ICON_PATH))

        layout = QVBoxLayout(dlg)
        layout.setSpacing(10)
        layout.setContentsMargins(12, 12, 12, 12)
        info = QLabel(text)
        info.setWordWrap(True)
        layout.addWidget(info)

        scroll, checks = _build_checklist_scroll(items)
        layout.addWidget(scroll)

        buttons = QDialogButtonBox()
        buttons.addButton(ok_label, QDialogButtonBox.ButtonRole.AcceptRole)
        buttons.addButton(cancel_label, QDialogButtonBox.ButtonRole.RejectRole)
        buttons.accepted.connect(dlg.accept)
        buttons.rejected.connect(dlg.reject)
        layout.addWidget(buttons)

        _auto_size(dlg, _items_width(items), height)

        if dlg.exec() == QDialog.DialogCode.Accepted:
            return [key for key, cb in checks if cb.isChecked()]
        return None

    def show_tabbed_checklist(
        self,
        title: str,
        global_text: str,
        global_items: list[ChecklistItem],
        usernames: list[str],
        per_user_text: str,
        per_user_items_fn: Callable[[str, set[str]], list[ChecklistItem]],
        ok_label: str = "OK",
        cancel_label: str = "Cancel",
        width: int = 0,
        height: int = 0,
    ) -> tuple[list[str], str, list[str]] | None:
        dlg = QDialog()
        dlg.setWindowTitle(title)
        dlg.setWindowIcon(QIcon(ICON_PATH))

        layout = QVBoxLayout(dlg)
        layout.setSpacing(10)
        layout.setContentsMargins(12, 12, 12, 12)

        tabs = QTabWidget()

        global_page = QWidget()
        g_layout = QVBoxLayout(global_page)
        g_layout.setSpacing(10)
        g_label = QLabel(global_text)
        g_label.setWordWrap(True)
        g_layout.addWidget(g_label)

        global_scroll, global_checks = _build_checklist_scroll(global_items)
        g_layout.addWidget(global_scroll)

        tabs.addTab(global_page, _("Global"))

        user_page = QWidget()
        u_layout = QVBoxLayout(user_page)
        u_layout.setSpacing(10)

        header = QHBoxLayout()
        header.addWidget(QLabel(_("User:")))
        combo = QComboBox()
        combo.addItems(usernames)
        header.addWidget(combo, 1)
        u_layout.addLayout(header)

        u_info = QLabel(per_user_text)
        u_info.setWordWrap(True)
        u_layout.addWidget(u_info)

        user_scroll_container = QVBoxLayout()
        u_layout.addLayout(user_scroll_container)

        user_checks: list[tuple[str, QCheckBox]] = []
        selected_user = [usernames[0] if usernames else ""]

        def _rebuild_user_list(uname: str) -> None:
            nonlocal user_checks
            selected_user[0] = uname
            while user_scroll_container.count():
                w = user_scroll_container.takeAt(0).widget()
                if w:
                    w.deleteLater()
            current_global = {key for key, cb in global_checks if cb.isChecked()}
            items = per_user_items_fn(uname, current_global)
            scroll, user_checks = _build_checklist_scroll(items)
            user_scroll_container.addWidget(scroll)

        initial_global = {item.key for item in global_items if item.checked}
        initial_per_user_snapshot: list[set[str]] = [set()]

        def _check_changes() -> None:
            current_global = {key for key, cb in global_checks if cb.isChecked()}
            changed = current_global != initial_global
            if not changed and user_checks:
                current_user = {key for key, cb in user_checks if cb.isChecked()}
                changed = current_user != initial_per_user_snapshot[0]
            btn_ok.setEnabled(changed)

        for _key, cb in global_checks:
            cb.stateChanged.connect(lambda _s: _check_changes())

        def _rebuild_and_track(uname: str) -> None:
            _rebuild_user_list(uname)
            initial_per_user_snapshot[0] = {
                key for key, cb in user_checks if cb.isChecked()
            }
            for _key, cb in user_checks:
                cb.stateChanged.connect(lambda _s: _check_changes())
            _check_changes()

        combo.currentTextChanged.connect(_rebuild_and_track)
        if usernames:
            _rebuild_and_track(usernames[0])

        tabs.addTab(user_page, _("Per-User"))

        def _on_tab_changed(index: int) -> None:
            if index == 1 and selected_user[0]:
                _rebuild_and_track(selected_user[0])

        tabs.currentChanged.connect(_on_tab_changed)
        layout.addWidget(tabs)

        buttons = QDialogButtonBox()
        btn_ok = buttons.addButton(ok_label, QDialogButtonBox.ButtonRole.AcceptRole)
        btn_ok.setEnabled(False)
        buttons.addButton(cancel_label, QDialogButtonBox.ButtonRole.RejectRole)
        buttons.accepted.connect(dlg.accept)
        buttons.rejected.connect(dlg.reject)
        layout.addWidget(buttons)

        w = _items_width(global_items) if global_items else 800
        _auto_size(dlg, w, height)

        if dlg.exec() == QDialog.DialogCode.Accepted:
            global_selected = [key for key, cb in global_checks if cb.isChecked()]
            user_selected = [key for key, cb in user_checks if cb.isChecked()]
            return (global_selected, selected_user[0], user_selected)
        return None

    def show_progress(
        self,
        title: str,
        text: str,
        callback: Callable[[], None],
    ) -> bool:
        dlg = QProgressDialog(text, None, 0, 0)
        dlg.setWindowTitle(title)
        dlg.setWindowIcon(QIcon(ICON_PATH))
        dlg.setWindowModality(Qt.WindowModality.ApplicationModal)
        dlg.setCancelButton(None)
        dlg.setMinimumDuration(0)

        success = {"value": False}

        def worker():
            try:
                callback()
                success["value"] = True
            except Exception:
                success["value"] = False
            finally:
                QTimer.singleShot(0, dlg.close)

        threading.Thread(target=worker, daemon=True).start()
        dlg.exec()
        return success["value"]
