"""Application entry point and Qt-style loading."""

from __future__ import annotations

import logging
import sys
from pathlib import Path

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication

from .main_window import BooruApp

log = logging.getLogger("booru")


def _apply_windows_dark_mode(app: QApplication) -> None:
    """Detect Windows dark mode and apply Fusion dark palette if needed."""
    try:
        import winreg
        key = winreg.OpenKey(
            winreg.HKEY_CURRENT_USER,
            r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
        )
        value, _ = winreg.QueryValueEx(key, "AppsUseLightTheme")
        winreg.CloseKey(key)
        if value == 0:
            from PySide6.QtGui import QPalette, QColor
            app.setStyle("Fusion")
            palette = QPalette()
            palette.setColor(QPalette.ColorRole.Window, QColor(32, 32, 32))
            palette.setColor(QPalette.ColorRole.WindowText, QColor(255, 255, 255))
            palette.setColor(QPalette.ColorRole.Base, QColor(25, 25, 25))
            palette.setColor(QPalette.ColorRole.AlternateBase, QColor(38, 38, 38))
            palette.setColor(QPalette.ColorRole.ToolTipBase, QColor(50, 50, 50))
            palette.setColor(QPalette.ColorRole.ToolTipText, QColor(255, 255, 255))
            palette.setColor(QPalette.ColorRole.Text, QColor(255, 255, 255))
            palette.setColor(QPalette.ColorRole.Button, QColor(51, 51, 51))
            palette.setColor(QPalette.ColorRole.ButtonText, QColor(255, 255, 255))
            palette.setColor(QPalette.ColorRole.BrightText, QColor(255, 0, 0))
            palette.setColor(QPalette.ColorRole.Link, QColor(0, 120, 215))
            palette.setColor(QPalette.ColorRole.Highlight, QColor(0, 120, 215))
            palette.setColor(QPalette.ColorRole.HighlightedText, QColor(255, 255, 255))
            palette.setColor(QPalette.ColorRole.Mid, QColor(51, 51, 51))
            palette.setColor(QPalette.ColorRole.Dark, QColor(25, 25, 25))
            palette.setColor(QPalette.ColorRole.Shadow, QColor(0, 0, 0))
            palette.setColor(QPalette.ColorRole.Light, QColor(60, 60, 60))
            palette.setColor(QPalette.ColorRole.Midlight, QColor(55, 55, 55))
            palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, QColor(127, 127, 127))
            palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor(127, 127, 127))
            app.setPalette(palette)
            # Flatten Fusion's 3D look
            app.setStyleSheet(app.styleSheet() + """
                QPushButton {
                    border: 1px solid #555;
                    border-radius: 2px;
                    padding: 4px 12px;
                }
                QPushButton:hover { background-color: #444; }
                QPushButton:pressed { background-color: #333; }
                QComboBox {
                    border: 1px solid #555;
                    border-radius: 2px;
                    padding: 3px 6px;
                }
                QComboBox::drop-down {
                    border: none;
                }
                QSpinBox {
                    border: 1px solid #555;
                    border-radius: 2px;
                }
                QLineEdit, QTextEdit {
                    border: 1px solid #555;
                    border-radius: 2px;
                    padding: 3px;
                    color: #fff;
                    background-color: #191919;
                }
                QScrollBar:vertical {
                    background: #252525;
                    width: 12px;
                }
                QScrollBar::handle:vertical {
                    background: #555;
                    border-radius: 4px;
                    min-height: 20px;
                }
                QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
                    height: 0;
                }
            """)
    except Exception as e:
        log.warning(f"Operation failed: {e}")


# Base popout overlay style — always loaded *before* the user QSS so the
# floating top toolbar (`#_slideshow_toolbar`) and bottom video controls
# (`#_slideshow_controls`) get a sane translucent-black-with-white-text
# look on themes that don't define their own overlay rules. Bundled themes
# in `themes/` redefine the same selectors with their @palette colors and
# win on tie (last rule of equal specificity wins in QSS), so anyone using
# a packaged theme keeps the themed overlay; anyone with a stripped-down
# custom.qss still gets a usable overlay instead of bare letterbox.
_BASE_POPOUT_OVERLAY_QSS = """
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls {
    background: rgba(0, 0, 0, 160);
}
QWidget#_slideshow_toolbar *,
QWidget#_slideshow_controls * {
    background: transparent;
    color: white;
    border: none;
}
QWidget#_slideshow_toolbar QPushButton,
QWidget#_slideshow_controls QPushButton {
    background: transparent;
    color: white;
    border: 1px solid rgba(255, 255, 255, 80);
    padding: 2px 6px;
    font-size: 15px;
    font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover {
    background: rgba(255, 255, 255, 30);
}
QWidget#_slideshow_toolbar QSlider::groove:horizontal,
QWidget#_slideshow_controls QSlider::groove:horizontal {
    background: rgba(255, 255, 255, 40);
    height: 4px;
    border: none;
}
QWidget#_slideshow_toolbar QSlider::handle:horizontal,
QWidget#_slideshow_controls QSlider::handle:horizontal {
    background: white;
    width: 10px;
    margin: -4px 0;
    border: none;
}
QWidget#_slideshow_toolbar QSlider::sub-page:horizontal,
QWidget#_slideshow_controls QSlider::sub-page:horizontal {
    background: white;
}
QWidget#_slideshow_toolbar QLabel,
QWidget#_slideshow_controls QLabel {
    background: transparent;
    color: white;
}
"""


def _load_user_qss(path: Path) -> str:
    """Load a QSS file with optional @palette variable substitution.

    Qt's QSS dialect has no native variables, so we add a tiny preprocessor:

        /* @palette
           accent:        #cba6f7
           bg:            #1e1e2e
           text:          #cdd6f4
        */

        QWidget {
            background-color: ${bg};
            color: ${text};
            selection-background-color: ${accent};
        }

    The header comment block is parsed for `name: value` pairs and any
    `${name}` reference elsewhere in the file is substituted with the
    corresponding value before the QSS is handed to Qt. This lets users
    recolor a bundled theme by editing the palette block alone, without
    hunting through the body for every hex literal.

    Backward compatibility: a file without an @palette block is returned
    as-is, so plain hand-written Qt-standard QSS still loads unchanged.
    Unknown ${name} references are left in place verbatim and logged as
    warnings so typos are visible in the log.
    """
    import re
    text = path.read_text()
    palette_match = re.search(r'/\*\s*@palette\b(.*?)\*/', text, re.DOTALL)
    if not palette_match:
        return text

    palette: dict[str, str] = {}
    for raw_line in palette_match.group(1).splitlines():
        # Strip leading whitespace and any leading * from C-style continuation
        line = raw_line.strip().lstrip('*').strip()
        if not line or ':' not in line:
            continue
        key, value = line.split(':', 1)
        key = key.strip()
        value = value.strip().rstrip(';').strip()
        # Allow trailing comments on the same line
        if '/*' in value:
            value = value.split('/*', 1)[0].strip()
        if key and value:
            palette[key] = value

    refs = set(re.findall(r'\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}', text))
    missing = refs - palette.keys()
    if missing:
        log.warning(
            f"QSS @palette: unknown vars {sorted(missing)} in {path.name} "
            f"— left in place verbatim, fix the @palette block to define them"
        )

    def replace(m):
        return palette.get(m.group(1), m.group(0))

    return re.sub(r'\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}', replace, text)


def run() -> None:
    from ..core.config import data_dir

    app = QApplication(sys.argv)

    # Set a stable Wayland app_id so Hyprland and other compositors can
    # consistently identify our windows by class (not by title, which
    # changes when search terms appear in the title bar). Qt translates
    # setDesktopFileName into the xdg-shell app_id on Wayland.
    app.setApplicationName("booru-viewer")
    app.setDesktopFileName("booru-viewer")

    # mpv requires LC_NUMERIC=C — Qt resets the locale in QApplication(),
    # so we must restore it after Qt init but before creating any mpv instances.
    import locale
    locale.setlocale(locale.LC_NUMERIC, "C")

    # Apply dark mode on Windows 10+ if system is set to dark
    if sys.platform == "win32":
        _apply_windows_dark_mode(app)

    # Load user custom stylesheet if it exists
    custom_css = data_dir() / "custom.qss"
    if custom_css.exists():
        try:
            # Use Fusion style with arrow color fix
            from PySide6.QtWidgets import QProxyStyle
            from PySide6.QtGui import QPalette, QColor, QPainter as _P
            from PySide6.QtCore import QPoint as _QP

            import re
            # Run through the @palette preprocessor (see _load_user_qss
            # for the dialect). Plain Qt-standard QSS files without an
            # @palette block are returned unchanged.
            css_text = _load_user_qss(custom_css)

            # Extract text color for arrows
            m = re.search(r'QWidget\s*\{[^}]*?(?:^|\s)color\s*:\s*(#[0-9a-fA-F]{3,8})', css_text, re.MULTILINE)
            arrow_color = QColor(m.group(1)) if m else QColor(200, 200, 200)

            class _DarkArrowStyle(QProxyStyle):
                """Fusion proxy that draws visible arrows on dark themes."""
                def drawPrimitive(self, element, option, painter, widget=None):
                    if element in (self.PrimitiveElement.PE_IndicatorSpinUp,
                                   self.PrimitiveElement.PE_IndicatorSpinDown,
                                   self.PrimitiveElement.PE_IndicatorArrowDown,
                                   self.PrimitiveElement.PE_IndicatorArrowUp):
                        painter.save()
                        painter.setRenderHint(_P.RenderHint.Antialiasing)
                        painter.setPen(Qt.PenStyle.NoPen)
                        painter.setBrush(arrow_color)
                        r = option.rect
                        cx, cy = r.center().x(), r.center().y()
                        s = min(r.width(), r.height()) // 3
                        from PySide6.QtGui import QPolygon
                        if element in (self.PrimitiveElement.PE_IndicatorSpinUp,
                                       self.PrimitiveElement.PE_IndicatorArrowUp):
                            painter.drawPolygon(QPolygon([
                                _QP(cx, cy - s), _QP(cx - s, cy + s), _QP(cx + s, cy + s)
                            ]))
                        else:
                            painter.drawPolygon(QPolygon([
                                _QP(cx - s, cy - s), _QP(cx + s, cy - s), _QP(cx, cy + s)
                            ]))
                        painter.restore()
                        return
                    super().drawPrimitive(element, option, painter, widget)

            app.setStyle(_DarkArrowStyle("Fusion"))
            # Prepend the base overlay defaults so even minimal custom.qss
            # files get a usable popout overlay. User rules with the same
            # selectors come last and win on tie.
            app.setStyleSheet(_BASE_POPOUT_OVERLAY_QSS + "\n" + css_text)

            # Extract selection color for grid highlight
            pal = app.palette()
            m = re.search(r'selection-background-color\s*:\s*(#[0-9a-fA-F]{3,8})', css_text)
            if m:
                pal.setColor(QPalette.ColorRole.Highlight, QColor(m.group(1)))
            app.setPalette(pal)
        except Exception as e:
            log.warning(f"Operation failed: {e}")
    else:
        # No custom.qss — force Fusion widgets so distro pyside6 builds linked
        # against system Qt don't pick up Breeze (or whatever the platform
        # theme plugin supplies) and diverge from the bundled-Qt look that
        # source-from-pip users get. The inherited palette is intentionally
        # left alone: KDE writes ~/.config/Trolltech.conf which every Qt app
        # reads, so KDE users still get their color scheme — just under
        # Fusion widgets instead of Breeze.
        app.setStyle("Fusion")
        # Install the popout overlay defaults so the floating toolbar/controls
        # have a sane background instead of bare letterbox color.
        app.setStyleSheet(_BASE_POPOUT_OVERLAY_QSS)

    # Set app icon (works in taskbar on all platforms)
    from PySide6.QtGui import QIcon
    # PyInstaller sets _MEIPASS for bundled data
    base_dir = Path(getattr(sys, '_MEIPASS', Path(__file__).parent.parent.parent))
    icon_path = base_dir / "icon.png"
    if not icon_path.exists():
        icon_path = Path(__file__).parent.parent.parent / "icon.png"
    if not icon_path.exists():
        icon_path = data_dir() / "icon.png"
    if icon_path.exists():
        app.setWindowIcon(QIcon(str(icon_path)))

    window = BooruApp()
    window.show()
    sys.exit(app.exec())
