# Copyright 2026, Jiri Eischmann <jiri@eischmann.cz> and the meshy contributors
# SPDX-License-Identifier: GPL-3.0-or-later

import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Adw, Gdk, Gtk, Gio


THEMES = {
    'meshcore': {
        'name': 'MeshCore Dark',
        'foreground': (1.0, 1.0, 1.0, 0.9),
        'background': (0.067, 0.094, 0.153),
        'accent': (0.231, 0.510, 0.965),
        'is_dark': True,
    },
    'meshcore-light': {
        'name': 'MeshCore Light',
        'foreground': (0.067, 0.094, 0.153, 0.85),
        'background': (0.945, 0.961, 0.976),
        'accent': (0.231, 0.510, 0.965),
        'is_dark': False,
    },
    'spring': {
        'name': 'Spring',
        'foreground': (0.250, 0.368, 0.501),
        'background': (0.972, 1.0, 0.949),
        'accent': (0.2, 0.65, 0.4),
        'is_dark': False,
    },
    'midnight': {
        'name': 'Midnight',
        'foreground': (1.0, 1.0, 1.0, 0.6),
        'background': (0.137, 0.207, 0.250),
        'accent': (0.160, 0.470, 0.650),
        'is_dark': True,
    },
    'gruvbox': {
        'name': 'Gruvbox',
        'foreground': (0.984, 0.945, 0.780, 0.8),
        'background': (0.156, 0.156, 0.156),
        'accent': (0.407, 0.615, 0.415),
        'is_dark': True,
    },
    'ocean': {
        'name': 'Ocean',
        'foreground': (0.741, 0.901, 0.984),
        'background': (0.117, 0.145, 0.160),
        'accent': (0.3, 0.7, 0.8),
        'is_dark': True,
    },
}

COLOR_SCHEME_MAP = {
    'system': Adw.ColorScheme.DEFAULT,
    'light': Adw.ColorScheme.FORCE_LIGHT,
    'dark': Adw.ColorScheme.FORCE_DARK,
}


class ThemeManager:
    _instance = None

    def __init__(self):
        self._recoloring_provider = Gtk.CssProvider()
        Gtk.StyleContext.add_provider_for_display(
            Gdk.Display.get_default(),
            self._recoloring_provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + 1,
        )
        self._settings = Gio.Settings(schema_id='page.codeberg.sesivany.Meshy')

    @classmethod
    def get_default(cls):
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance

    def apply_from_settings(self):
        mode = self._settings.get_string('color-scheme')
        if mode == 'custom':
            theme_id = self._settings.get_string('custom-theme')
            self.apply_custom_theme(theme_id)
        else:
            self.apply_color_scheme(mode)

    def apply_color_scheme(self, mode):
        scheme = COLOR_SCHEME_MAP.get(mode, Adw.ColorScheme.DEFAULT)
        Adw.StyleManager.get_default().set_color_scheme(scheme)
        self._recoloring_provider.load_from_string('')

    def apply_custom_theme(self, theme_id):
        theme = THEMES.get(theme_id)
        if not theme:
            Adw.StyleManager.get_default().set_color_scheme(Adw.ColorScheme.DEFAULT)
            self._recoloring_provider.load_from_string('')
            return
        if theme['is_dark']:
            Adw.StyleManager.get_default().set_color_scheme(Adw.ColorScheme.FORCE_DARK)
        else:
            Adw.StyleManager.get_default().set_color_scheme(Adw.ColorScheme.FORCE_LIGHT)
        css = self._generate_css(theme)
        self._recoloring_provider.load_from_string(css)

    def set_color_scheme(self, mode):
        self._settings.set_string('color-scheme', mode)
        if mode != 'custom':
            self.apply_color_scheme(mode)

    def set_custom_theme(self, theme_id):
        self._settings.set_string('custom-theme', theme_id)
        self._settings.set_string('color-scheme', 'custom')
        self.apply_custom_theme(theme_id)

    @staticmethod
    def _rgba_str(components):
        if len(components) == 4:
            r, g, b, a = components
            return f'rgba({int(r*255)},{int(g*255)},{int(b*255)},{a:.2f})'
        r, g, b = components[:3]
        return f'rgb({int(r*255)},{int(g*255)},{int(b*255)})'

    @staticmethod
    def _rgba_opaque_str(components):
        r, g, b = components[:3]
        return f'rgb({int(r*255)},{int(g*255)},{int(b*255)})'

    def _generate_css(self, theme):
        fg = theme['foreground']
        bg = theme['background']
        accent = theme['accent']
        is_dark = theme['is_dark']

        alt = 'white' if is_dark else 'black'
        mix_level = 0.07 if is_dark else 0.25

        fg_str = self._rgba_str(fg)
        bg_str = self._rgba_opaque_str(bg)

        lines = []
        lines.append(f'@define-color window_bg_color {bg_str};')
        lines.append(f'@define-color window_fg_color {fg_str};')
        lines.append(f'@define-color headerbar_bg_color mix({bg_str},white,{mix_level});')
        lines.append(f'@define-color headerbar_fg_color {fg_str};')
        lines.append(f'@define-color popover_bg_color mix({bg_str},white,{mix_level});')
        lines.append(f'@define-color popover_fg_color {fg_str};')
        lines.append(f'@define-color view_bg_color mix({bg_str},{"black" if is_dark else "white"},{0.1 if is_dark else 0.3});')
        lines.append(f'@define-color view_fg_color {fg_str};')
        lines.append(f'@define-color card_fg_color {fg_str};')
        lines.append(f'@define-color headerbar_border_color {fg_str};')
        lines.append(f'@define-color dialog_fg_color {fg_str};')
        lines.append(f'@define-color dark_fill_bg_color @headerbar_bg_color;')

        if is_dark:
            lines.append(f'@define-color dialog_bg_color mix({bg_str},white,0.07);')
            lines.append('@define-color card_bg_color alpha(white,0.08);')
        else:
            lines.append(f'@define-color dialog_bg_color {bg_str};')
            lines.append('@define-color card_bg_color alpha(white,0.8);')

        lines.append(f'@define-color sidebar_bg_color mix({bg_str},{alt},0.05);')
        lines.append(f'@define-color sidebar_backdrop_color mix({bg_str},{alt},0.1);')
        lines.append(f'@define-color sidebar_fg_color mix({fg_str},{alt},0.05);')
        lines.append(f'@define-color sidebar_shade_color mix({bg_str},black,0.1);')
        lines.append(f'@define-color sidebar_border_color mix({bg_str},black,0.1);')

        lines.append(f'@define-color secondary_sidebar_bg_color mix({bg_str},{alt},0.02);')
        lines.append(f'@define-color secondary_sidebar_backdrop_color mix({bg_str},{alt},0.05);')
        lines.append(f'@define-color secondary_sidebar_fg_color mix({fg_str},{alt},0.02);')
        lines.append(f'@define-color secondary_sidebar_shade_color mix({bg_str},black,0.1);')
        lines.append(f'@define-color secondary_sidebar_border_color mix({bg_str},black,0.1);')

        if accent:
            accent_str = self._rgba_opaque_str(accent)
            lines.append(f'@define-color accent_color {accent_str};')
            lines.append(f'@define-color accent_bg_color {accent_str};')
            lines.append('@define-color accent_fg_color white;')

        return '\n'.join(lines)
