from __future__ import annotations

from dataclasses import dataclass, field
from functools import partial
from operator import attrgetter
from typing import Callable

from textual.command import DiscoveryHit, Hit, Hits, Provider
from textual.design import ColorSystem


@dataclass
class Theme:
    """Defines a theme for the application."""

    name: str
    """The name of the theme.

    After registering a theme with `App.register_theme`, you can set the theme with
    `App.theme = theme_name`. This will immediately apply the theme's colors to your
    application.
    """

    primary: str
    secondary: str | None = None
    warning: str | None = None
    error: str | None = None
    success: str | None = None
    accent: str | None = None
    foreground: str | None = None
    background: str | None = None
    surface: str | None = None
    panel: str | None = None
    boost: str | None = None
    dark: bool = True
    luminosity_spread: float = 0.15
    text_alpha: float = 0.95
    variables: dict[str, str] = field(default_factory=dict)

    ansi: bool = False
    """False` to disable native ansi colors, `True` to enable native ansi"""

    def to_color_system(self) -> ColorSystem:
        """
        Create a ColorSystem instance from this Theme.

        Returns:
            A ColorSystem instance with attributes copied from this Theme.
        """
        return ColorSystem(
            primary=self.primary,
            secondary=self.secondary,
            warning=self.warning,
            error=self.error,
            success=self.success,
            accent=self.accent,
            foreground=self.foreground,
            background=self.background,
            surface=self.surface,
            panel=self.panel,
            boost=self.boost,
            dark=self.dark,
            luminosity_spread=self.luminosity_spread,
            text_alpha=self.text_alpha,
            variables=self.variables,
            ansi=self.ansi,
        )


BUILTIN_THEMES: dict[str, Theme] = {
    "textual-dark": Theme(
        name="textual-dark",
        primary="#0178D4",
        secondary="#004578",
        accent="#ffa62b",
        warning="#ffa62b",
        error="#ba3c5b",
        success="#4EBF71",
        foreground="#e0e0e0",
    ),
    "textual-light": Theme(
        name="textual-light",
        primary="#004578",
        secondary="#0178D4",
        accent="#ffa62b",
        warning="#ffa62b",
        error="#ba3c5b",
        success="#4EBF71",
        surface="#D8D8D8",
        panel="#D0D0D0",
        background="#E0E0E0",
        dark=False,
        variables={
            "footer-key-foreground": "#0178D4",
        },
    ),
    "nord": Theme(
        name="nord",
        primary="#88C0D0",
        secondary="#81A1C1",
        accent="#B48EAD",
        foreground="#D8DEE9",
        background="#2E3440",
        success="#A3BE8C",
        warning="#EBCB8B",
        error="#BF616A",
        surface="#3B4252",
        panel="#434C5E",
        variables={
            "block-cursor-background": "#88C0D0",
            "block-cursor-foreground": "#2E3440",
            "block-cursor-text-style": "none",
            "footer-key-foreground": "#88C0D0",
            "input-selection-background": "#81a1c1 35%",
            "button-color-foreground": "#2E3440",
            "button-focus-text-style": "reverse",
        },
    ),
    "gruvbox": Theme(
        name="gruvbox",
        primary="#85A598",
        secondary="#A89A85",
        warning="#fe8019",
        error="#fb4934",
        success="#b8bb26",
        accent="#fabd2f",
        foreground="#fbf1c7",
        background="#282828",
        surface="#3c3836",
        panel="#504945",
        variables={
            "block-cursor-foreground": "#fbf1c7",
            "input-selection-background": "#689d6a40",
            "button-color-foreground": "#282828",
        },
    ),
    "catppuccin-mocha": Theme(
        name="catppuccin-mocha",
        primary="#F5C2E7",
        secondary="#cba6f7",
        warning="#FAE3B0",
        error="#F28FAD",
        success="#ABE9B3",
        accent="#fab387",
        foreground="#cdd6f4",
        background="#181825",
        surface="#313244",
        panel="#45475a",
        variables={
            "input-cursor-foreground": "#11111b",
            "input-cursor-background": "#f5e0dc",
            "input-selection-background": "#9399b2 30%",
            "border": "#b4befe",
            "border-blurred": "#585b70",
            "footer-background": "#45475a",
            "block-cursor-foreground": "#1e1e2e",
            "block-cursor-text-style": "none",
            "button-color-foreground": "#181825",
        },
    ),
    "dracula": Theme(
        name="dracula",
        primary="#BD93F9",
        secondary="#6272A4",
        warning="#FFB86C",
        error="#FF5555",
        success="#50FA7B",
        accent="#FF79C6",
        background="#282A36",
        surface="#2B2E3B",
        panel="#313442",
        foreground="#F8F8F2",
        variables={
            "button-color-foreground": "#282A36",
        },
    ),
    "tokyo-night": Theme(
        name="tokyo-night",
        primary="#BB9AF7",
        secondary="#7AA2F7",
        warning="#E0AF68",  # Yellow
        error="#F7768E",  # Red
        success="#9ECE6A",  # Green
        accent="#FF9E64",  # Orange
        foreground="#a9b1d6",
        background="#1A1B26",  # Background
        surface="#24283B",  # Surface
        panel="#414868",  # Panel
        variables={
            "button-color-foreground": "#24283B",
        },
    ),
    "monokai": Theme(
        name="monokai",
        primary="#AE81FF",
        secondary="#F92672",
        accent="#66D9EF",
        warning="#FD971F",
        error="#F92672",
        success="#A6E22E",
        foreground="#d6d6d6",
        background="#272822",
        surface="#2e2e2e",
        panel="#3E3D32",
        variables={
            "foreground-muted": "#797979",
            "input-selection-background": "#575b6190",
            "button-color-foreground": "#272822",
        },
    ),
    "flexoki": Theme(
        name="flexoki",
        primary="#205EA6",  # blue
        secondary="#24837B",  # cyan
        warning="#AD8301",  # yellow
        error="#AF3029",  # red
        success="#66800B",  # green
        accent="#9B76C8",  # purple light
        background="#100F0F",  # base.black
        surface="#1C1B1A",  # base.950
        panel="#282726",  # base.900
        foreground="#FFFCF0",  # base.paper
        variables={
            "input-cursor-foreground": "#5E409D",
            "input-cursor-background": "#FFFCF0",
            "input-selection-background": "#6F6E69 35%",  # base.600 with opacity
            "button-color-foreground": "#FFFCF0",
        },
    ),
    "catppuccin-latte": Theme(
        name="catppuccin-latte",
        secondary="#DC8A78",
        primary="#8839EF",
        warning="#DF8E1D",
        error="#D20F39",
        success="#40A02B",
        accent="#FE640B",
        foreground="#4C4F69",
        background="#EFF1F5",
        surface="#E6E9EF",
        panel="#CCD0DA",
        dark=False,
        variables={
            "button-color-foreground": "#EFF1F5",
        },
    ),
    "catppuccin-frappe": Theme(
        name="catppuccin-frappe",
        primary="#CA9EE6",
        secondary="#EF9F76",
        warning="#E5C890",
        error="#E78284",
        success="#A6D189",
        accent="#F4B8E4",
        foreground="#C6D0F5",
        background="#303446",
        surface="#414559",
        panel="#51576D",
        dark=True,
        variables={
            "input-cursor-foreground": "#232634",
            "input-cursor-background": "#F2D5CF",
            "input-selection-background": "#949CBB 30%",
            "border": "#BABBF1",
            "border-blurred": "#838BA7",
            "footer-background": "#51576D",
            "block-cursor-foreground": "#292C3C",
            "block-cursor-text-style": "none",
            "button-color-foreground": "#303446",
        },
    ),
    "catppuccin-macchiato": Theme(
        name="catppuccin-macchiato",
        primary="#C6A0F6",
        secondary="#F5A97F",
        warning="#EED49F",
        error="#ED8796",
        success="#A6DA95",
        accent="#F5BDE6",
        foreground="#CAD3F5",
        background="#24273A",
        surface="#363A4F",
        panel="#494D64",
        dark=True,
        variables={
            "input-cursor-foreground": "#181926",
            "input-cursor-background": "#F4DBD6",
            "input-selection-background": "#838BA7 30%",
            "border": "#B7BDF8",
            "border-blurred": "#737994",
            "footer-background": "#494D64",
            "block-cursor-foreground": "#1E2030",
            "block-cursor-text-style": "none",
            "button-color-foreground": "#24273A",
        },
    ),
    "solarized-light": Theme(
        name="solarized-light",
        primary="#268bd2",
        secondary="#2aa198",
        warning="#cb4b16",
        error="#dc322f",
        success="#859900",
        accent="#6c71c4",
        foreground="#586e75",
        background="#fdf6e3",
        surface="#eee8d5",
        panel="#eee8d5",
        dark=False,
        variables={
            "button-color-foreground": "#fdf6e3",
            "footer-background": "#268bd2",
            "footer-key-foreground": "#fdf6e3",
            "footer-description-foreground": "#fdf6e3",
        },
    ),
    "solarized-dark": Theme(
        name="solarized-dark",
        primary="#268bd2",
        secondary="#2aa198",
        warning="#cb4b16",
        error="#dc322f",
        success="#859900",
        accent="#6c71c4",
        background="#002b36",
        surface="#073642",
        panel="#073642",
        foreground="#839496",
        dark=True,
        variables={
            "button-color-foreground": "#fdf6e3",
            "footer-background": "#268bd2",
            "footer-key-foreground": "#fdf6e3",
            "footer-description-foreground": "#fdf6e3",
            "input-selection-background": "#073642",  # Base02
        },
    ),
    "rose-pine": Theme(
        name="rose-pine",
        primary="#c4a7e7",
        secondary="#31748f",
        warning="#f6c177",
        error="#eb6f92",
        success="#9ccfd8",
        accent="#ebbcba",
        foreground="#e0def4",
        background="#191724",
        surface="#1f1d2e",
        panel="#26233a",
        dark=True,
        variables={
            "input-cursor-background": "#f4ede8",
            "input-selection-background": "#403d52",
            "border": "#524f67",
            "border-blurred": "#6e6a86",
            "footer-background": "#26233a",
            "block-cursor-foreground": "#191724",
            "block-cursor-text-style": "none",
            "block-cursor-background": "#c4a7e7",
        },
    ),
    "rose-pine-moon": Theme(
        name="rose-pine-moon",
        primary="#c4a7e7",
        secondary="#3e8fb0",
        warning="#f6c177",
        error="#eb6f92",
        success="#9ccfd8",
        accent="#ea9a97",
        foreground="#e0def4",
        background="#232136",
        surface="#2a273f",
        panel="#393552",
        dark=True,
        variables={
            "input-cursor-background": "#f4ede8",
            "input-selection-background": "#44415a",
            "border": "#56526e",
            "border-blurred": "#6e6a86",
            "footer-background": "#393552",
            "block-cursor-foreground": "#232136",
            "block-cursor-text-style": "none",
            "block-cursor-background": "#c4a7e7",
        },
    ),
    "rose-pine-dawn": Theme(
        name="rose-pine-dawn",
        primary="#907aa9",
        secondary="#286983",
        warning="#ea9d34",
        error="#b4637a",
        success="#56949f",
        accent="#d7827e",
        foreground="#575279",
        background="#faf4ed",
        surface="#fffaf3",
        panel="#f2e9e1",
        dark=False,
        variables={
            "input-cursor-background": "#575279",
            "input-selection-background": "#dfdad9",
            "border": "#cecacd",
            "border-blurred": "#9893a5",
            "footer-background": "#f2e9e1",
            "block-cursor-foreground": "#faf4ed",
            "block-cursor-text-style": "none",
            "block-cursor-background": "#575279",
        },
    ),
    "atom-one-dark": Theme(
        name="atom-one-dark",
        primary="#61AFEF",
        secondary="#C678DD",
        warning="#DEB25B",
        error="#F06262",
        success="#62F062",
        accent="#A378C2",
        foreground="#ABB2BF",
        background="#282C34",
        surface="#3B414D",
        panel="#4F5666",
        boost=None,
        dark=True,
        luminosity_spread=0.15,
        text_alpha=0.95,
    ),
    "atom-one-light": Theme(
        name="atom-one-light",
        primary="#4078F2",
        secondary="#A626A4",
        warning="#D8D938",
        error="#F23F3F",
        success="#6CF23F",
        accent="#bf9232",
        foreground="#383A42",
        background="#FAFAFA",
        surface="#E0E0E0",
        panel="#CCCCCC",
        boost=None,
        dark=False,
        luminosity_spread=0.15,
        text_alpha=0.95,
    ),
    "ansi-dark": Theme(
        name="ansi-dark",
        ansi=True,
        primary="ansi_blue",
        secondary="ansi_cyan",
        warning="ansi_yellow",
        error="ansi_red",
        success="ansi_green",
        accent="ansi_green",
        foreground="ansi_default",
        background="ansi_default",
        surface="ansi_default",
        panel="ansi_default",
        boost="ansi_default",
        dark=True,
        variables={
            "ansi-background": "ansi_black",
            "ansi-foreground": "ansi_white",
            "border-blurred": "ansi_black",
            "block-cursor-foreground": "ansi_black",
            "block-cursor-background": "ansi_white",
            "input-cursor-background": "ansi_black",
            "input-cursor-foreground": "ansi_bright_white",
            "input-cursor-text-style": "none",
            "input-selection-background": "ansi_bright_blue",
            "input-selection-foreground": "ansi_black",
            "screen-selection-background": "ansi_bright_blue",
            "screen-selection-foreground": "ansi_black",
        },
    ),
    "ansi-light": Theme(
        name="ansi-light",
        ansi=True,
        primary="ansi_blue",
        secondary="ansi_cyan",
        warning="ansi_bright_red",
        error="ansi_red",
        success="ansi_green",
        accent="ansi_magenta",
        foreground="ansi_default",
        background="ansi_default",
        surface="ansi_default",
        panel="ansi_default",
        boost="ansi_default",
        dark=False,
        variables={
            "border": "ansi_blue",
            "border-blurred": "ansi_white",
            "block-cursor-foreground": "ansi_bright_white",
            "block-cursor-background": "ansi_blue",
            "ansi-background": "ansi_white",
            "ansi-foreground": "ansi_black",
            "input-cursor-background": "ansi_bright_white",
            "input-cursor-foreground": "ansi_black",
            "input-cursor-text-style": "reverse",
            "input-selection-background": "ansi_cyan",
            "input-selection-foreground": "ansi_white",
            "scrollbar": "ansi_bright_blue",
            "scrollbar-hover": "ansi_blue",
            "scrollbar-active": "ansi_blue",
            "scrollbar-background": "ansi_white",
            "scrollbar-corner-color": "ansi_default",
            "scrollbar-background-hover": "ansi_white",
            "scrollbar-background-active": "ansi_white",
            "block-hover-background": "ansi_white",
            "screen-selection-background": "ansi_cyan",
            "screen-selection-foreground": "ansi_bright_white",
        },
    ),
}


class ThemeProvider(Provider):
    """A provider for themes."""

    @property
    def commands(self) -> list[tuple[str, Callable[[], None]]]:
        themes = self.app.available_themes

        def set_app_theme(name: str) -> None:
            self.app.theme = name

        return [
            (theme.name, partial(set_app_theme, theme.name))
            for theme in sorted(themes.values(), key=attrgetter("name"))
        ]

    async def discover(self) -> Hits:
        for command in self.commands:
            yield DiscoveryHit(*command)

    async def search(self, query: str) -> Hits:
        matcher = self.matcher(query)

        for name, callback in self.commands:
            if (match := matcher.match(name)) > 0:
                yield Hit(
                    match,
                    matcher.highlight(name),
                    callback,
                )
