from __future__ import annotations

from dataclasses import dataclass

from PySide6.QtCore import Qt
from PySide6.QtGui import QColor, QPalette
from PySide6.QtWidgets import QApplication


THEME_SYSTEM = "system"
THEME_LIGHT = "light"
THEME_DARK = "dark"

THEME_OPTIONS: list[tuple[str, str]] = [
    ("System", THEME_SYSTEM),
    ("Light", THEME_LIGHT),
    ("Dark", THEME_DARK),
]


@dataclass(frozen=True)
class ThemeColors:
    is_dark: bool
    window_bg: str
    surface_bg: str
    alt_surface_bg: str
    text: str
    muted_text: str
    subtle_text: str
    border: str
    border_strong: str
    table_grid: str
    detail_alt_row: str
    selection: str
    selection_active: str
    selected_text: str
    code_bg: str
    link: str
    tooltip_bg: str
    tooltip_text: str
    tooltip_border: str
    tag_default: str
    danger: str
    warning: str
    success: str


@dataclass(frozen=True)
class ThemeContext:
    preference: str
    resolved_theme: str
    colors: ThemeColors


def normalize_theme_preference(value: object) -> str:
    text = str(value or "").strip().lower()
    if text in {THEME_LIGHT, THEME_DARK, THEME_SYSTEM}:
        return text
    return THEME_SYSTEM


def apply_application_theme(app: QApplication, preference: str) -> ThemeContext:
    preference = normalize_theme_preference(preference)
    style_hints = app.styleHints()

    if preference == THEME_SYSTEM:
        style_hints.setColorScheme(Qt.ColorScheme.Unknown)
    else:
        style_hints.setColorScheme(
            Qt.ColorScheme.Dark if preference == THEME_DARK else Qt.ColorScheme.Light
        )

    resolved_theme = _resolve_theme_name(app, preference)
    colors = DARK_COLORS if resolved_theme == THEME_DARK else LIGHT_COLORS

    app.setPalette(build_palette(resolved_theme))

    return ThemeContext(
        preference=preference,
        resolved_theme=resolved_theme,
        colors=colors,
    )


def build_palette(theme_name: str) -> QPalette:
    theme_name = THEME_DARK if theme_name == THEME_DARK else THEME_LIGHT
    colors = DARK_COLORS if theme_name == THEME_DARK else LIGHT_COLORS
    palette = QPalette()

    window = QColor(colors.window_bg)
    base = QColor(colors.surface_bg)
    alt_base = QColor(colors.alt_surface_bg)
    text = QColor(colors.text)
    muted = QColor(colors.muted_text)
    border = QColor(colors.border)
    selection = QColor(colors.selection)
    selection_active = QColor(colors.selection_active)
    tooltip_bg = QColor(colors.tooltip_bg)
    tooltip_text = QColor(colors.tooltip_text)

    palette.setColor(QPalette.ColorRole.Window, window)
    palette.setColor(QPalette.ColorRole.WindowText, text)
    palette.setColor(QPalette.ColorRole.Base, base)
    palette.setColor(QPalette.ColorRole.AlternateBase, alt_base)
    palette.setColor(QPalette.ColorRole.ToolTipBase, tooltip_bg)
    palette.setColor(QPalette.ColorRole.ToolTipText, tooltip_text)
    palette.setColor(QPalette.ColorRole.Text, text)
    palette.setColor(QPalette.ColorRole.Button, window)
    palette.setColor(QPalette.ColorRole.ButtonText, text)
    palette.setColor(QPalette.ColorRole.BrightText, QColor("#ffffff"))
    palette.setColor(QPalette.ColorRole.Highlight, selection)
    palette.setColor(QPalette.ColorRole.HighlightedText, QColor(colors.selected_text))
    palette.setColor(QPalette.ColorRole.PlaceholderText, muted)
    palette.setColor(QPalette.ColorRole.Mid, border)
    palette.setColor(QPalette.ColorRole.Midlight, _mix(window, text, 0.24 if colors.is_dark else 0.18))
    palette.setColor(QPalette.ColorRole.Light, _mix(window, text, 0.12 if colors.is_dark else 0.08))
    palette.setColor(QPalette.ColorRole.Shadow, QColor("#000000"))
    palette.setColor(QPalette.ColorRole.Link, QColor(colors.link))
    palette.setColor(QPalette.ColorRole.LinkVisited, QColor(colors.link))

    disabled_text = QColor("#8d949c") if colors.is_dark else QColor("#8b93a1")
    palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, disabled_text)
    palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, disabled_text)
    palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, disabled_text)
    palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, QColor(colors.selected_text))
    palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Highlight, selection_active)
    return palette


def system_uses_dark_theme(app: QApplication) -> bool:
    scheme = app.styleHints().colorScheme()
    return scheme == Qt.ColorScheme.Dark


def _resolve_theme_name(app: QApplication, preference: str) -> str:
    if preference == THEME_DARK:
        return THEME_DARK
    if preference == THEME_LIGHT:
        return THEME_LIGHT
    return THEME_DARK if system_uses_dark_theme(app) else THEME_LIGHT


def _mix(a: QColor, b: QColor, amount: float) -> QColor:
    amount = max(0.0, min(1.0, amount))
    inv = 1.0 - amount
    return QColor(
        round((a.red() * inv) + (b.red() * amount)),
        round((a.green() * inv) + (b.green() * amount)),
        round((a.blue() * inv) + (b.blue() * amount)),
    )


DARK_COLORS = ThemeColors(
    is_dark=True,
    window_bg="#202124",
    surface_bg="#17181a",
    alt_surface_bg="#26282c",
    text="#f1f3f4",
    muted_text="#c1c7d0",
    subtle_text="#9098a3",
    border="#4b4f56",
    border_strong="#5c6169",
    table_grid="#2a2d31",
    detail_alt_row="#23262a",
    selection="#404552",
    selection_active="#4b5263",
    selected_text="#f1f3f4",
    code_bg="#292b2e",
    link="#8ab4ff",
    tooltip_bg="#2a2c30",
    tooltip_text="#f1f3f4",
    tooltip_border="#5c6169",
    tag_default="#f1f3f4",
    danger="#ff6b6b",
    warning="#feca57",
    success="#1dd1a1",
)


LIGHT_COLORS = ThemeColors(
    is_dark=False,
    window_bg="#f5f6f8",
    surface_bg="#ffffff",
    alt_surface_bg="#e8eaec",
    text="#1f2329",
    muted_text="#4b5563",
    subtle_text="#6b7280",
    border="#e2e6eb",
    border_strong="#d4dae2",
    table_grid="#eceff3",
    detail_alt_row="#eef1f4",
    selection="#e3e6ea",
    selection_active="#d9dde2",
    selected_text="#1f2329",
    code_bg="#f4f5f7",
    link="#0b57d0",
    tooltip_bg="#ffffff",
    tooltip_text="#1f2329",
    tooltip_border="#d7dde5",
    tag_default="#1f2329",
    danger="#e9491e",
    warning="#ff9600",
    success="#0f9754",
)
