# Copyright (c) 2025, 2026 Manuel Ochoa
# This file is part of CommStat.
# Licensed under the GNU General Public License v3.0.
"""
ui_helpers.py - Shared UI helpers for CommStat dialogs.

Centralizes the styled QPushButton, QLineEdit, and centered-checkbox-cell
factories that were previously copy-pasted across every dialog module.
The canonical implementation comes from qrz_settings.py.
"""

import os
from typing import Tuple

from PyQt5 import QtGui
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import (
    QPushButton, QLineEdit, QCheckBox, QComboBox, QWidget, QHBoxLayout, QMessageBox,
)

from constants import (
    FONT_ROBOTO, FONT_MONO, FONT_ROBOTO_STACK, FONT_MONO_STACK,
    COLOR_BTN_GREEN, COLOR_BTN_GRAY, COLOR_BTN_BLUE,
    COLOR_INPUT_TEXT, COLOR_INPUT_BORDER,
    ICON_FILE,
)


# ── Button ─────────────────────────────────────────────────────────────────────

def make_button(label: str, color: str, min_w: int = 90) -> QPushButton:
    """Standard styled action button: Roboto Bold 15px, colored background."""
    b = QPushButton(label)
    b.setMinimumWidth(min_w)
    b.setStyleSheet(
        f"QPushButton {{ background-color:{color}; color:#ffffff; border:none;"
        f" padding:6px 14px; border-radius:4px; font-family:{FONT_ROBOTO_STACK}; font-size:15px;"
        f" font-weight:bold; }}"
        f"QPushButton:hover {{ background-color:{color}; opacity:0.9; }}"
        f"QPushButton:pressed {{ background-color:{color}; }}"
        f"QPushButton:disabled {{ background-color:#cccccc; color:#888888; }}"
    )
    return b


def connect_single(button, handler, window_ms: int = 1000):
    """Connect button.clicked to handler so rapid repeat clicks (e.g. a
    double-click) fire the handler only once. A second click within window_ms
    is ignored. Does NOT touch setEnabled(), so it never interferes with
    validation- or mode-driven enable/disable logic."""
    state = {"busy": False}

    def _wrapped(*_args):
        if state["busy"]:
            return
        state["busy"] = True
        try:
            handler()
        finally:
            QTimer.singleShot(window_ms, lambda: state.update(busy=False))

    button.clicked.connect(_wrapped)
    return _wrapped


# ── Confirmation dialog ───────────────────────────────────────────────────────

def confirm(parent, title: str, text: str,
            yes_label: str = "Yes", no_label: str = "No",
            default_yes: bool = False) -> bool:
    """Styled Yes/No confirmation. Returns True if user clicked the affirmative button."""
    box = QMessageBox(parent)
    box.setWindowTitle(title)
    box.setText(text)
    yes_btn = make_button(yes_label, COLOR_BTN_GREEN)
    no_btn = make_button(no_label, COLOR_BTN_GRAY)
    box.addButton(yes_btn, QMessageBox.YesRole)
    box.addButton(no_btn, QMessageBox.NoRole)
    box.setDefaultButton(yes_btn if default_yes else no_btn)
    box.exec_()
    return box.clickedButton() is yes_btn


# ── Input ──────────────────────────────────────────────────────────────────────

def make_input(placeholder: str = "", default: str = "", max_len: int = 0) -> QLineEdit:
    """Standard styled QLineEdit: white background, Kode Mono 13px."""
    e = QLineEdit()
    if placeholder:
        e.setPlaceholderText(placeholder)
    if default:
        e.setText(default)
    if max_len:
        e.setMaxLength(max_len)
    e.setMinimumHeight(30)
    e.setStyleSheet(
        f"QLineEdit {{ background-color:white; color:{COLOR_INPUT_TEXT}; border:1px solid {COLOR_INPUT_BORDER};"
        f" border-radius:4px; padding:2px 6px; font-family:{FONT_MONO_STACK}; font-size:13px; }}"
        f"QLineEdit:focus {{ border:1px solid {COLOR_BTN_BLUE}; }}"
    )
    return e


# ── Combo box ──────────────────────────────────────────────────────────────────

def make_combobox(items) -> QComboBox:
    """
    Standard styled QComboBox matching make_input.
    items: iterable of (label, value) tuples. Stored value via itemData.
    """
    cb = QComboBox()
    cb.setMinimumHeight(30)
    cb.setStyleSheet(
        f"QComboBox {{ background-color:white; color:{COLOR_INPUT_TEXT}; border:1px solid {COLOR_INPUT_BORDER};"
        f" border-radius:4px; padding:2px 6px; font-family:{FONT_MONO_STACK}; font-size:13px; }}"
        f"QComboBox:focus {{ border:1px solid {COLOR_BTN_BLUE}; }}"
        f"QComboBox QAbstractItemView {{ background-color:white; color:{COLOR_INPUT_TEXT};"
        " selection-background-color:#cce5ff; selection-color:#000000;"
        f" font-family:{FONT_MONO_STACK}; font-size:13px; }}"
    )
    for label, value in items:
        cb.addItem(label, value)
    return cb


# ── Checkbox cell ──────────────────────────────────────────────────────────────

def make_checkbox_cell(checked: bool = False) -> Tuple[QWidget, QCheckBox]:
    """Return (container_widget, checkbox) with the checkbox centered in the cell."""
    container = QWidget()
    container.setStyleSheet("background-color: transparent;")
    layout = QHBoxLayout(container)
    layout.setContentsMargins(0, 0, 0, 0)
    layout.setAlignment(Qt.AlignCenter)
    cb = QCheckBox()
    cb.setChecked(checked)
    cb.setStyleSheet(
        "QCheckBox { background-color: transparent; }"
        "QCheckBox::indicator { width:16px; height:16px; }"
    )
    layout.addWidget(cb)
    return container, cb


# ── Fonts ──────────────────────────────────────────────────────────────────────

def label_font() -> QtGui.QFont:
    """Roboto Bold — for QLabel headings within dialogs."""
    return QtGui.QFont(FONT_ROBOTO, -1, QtGui.QFont.Bold)


def mono_font() -> QtGui.QFont:
    """Kode Mono — for table cells and data display."""
    return QtGui.QFont(FONT_MONO)


# ── Dialog chrome ──────────────────────────────────────────────────────────────

def apply_standard_dialog_chrome(dialog, title: str, w: int = 0, h: int = 0) -> None:
    """Set window flags, title, optional fixed size, and app icon for any CommStat dialog."""
    dialog.setWindowFlags(
        Qt.Window |
        Qt.CustomizeWindowHint |
        Qt.WindowTitleHint |
        Qt.WindowCloseButtonHint |
        Qt.WindowStaysOnTopHint
    )
    dialog.setWindowTitle(title)
    if w and h:
        dialog.setFixedSize(w, h)
    if os.path.exists(ICON_FILE):
        dialog.setWindowIcon(QtGui.QIcon(ICON_FILE))
