import json
import os

from fabric.hyprland.service import HyprlandEvent
from fabric.hyprland.widgets import HyprlandLanguage as Language
from fabric.hyprland.widgets import HyprlandWorkspaces as Workspaces
from fabric.hyprland.widgets import WorkspaceButton, get_hyprland_connection
from fabric.utils.helpers import exec_shell_command_async
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.datetime import DateTime
from fabric.widgets.label import Label
from fabric.widgets.revealer import Revealer
from gi.repository import Gdk, GLib, Gtk

import config.data as data
import modules.icons as icons
from modules.controls import ControlSmall
from modules.dock import Dock
from modules.metrics import Battery, MetricsSmall, NetworkApplet
from modules.systemprofiles import Systemprofiles
from modules.systemtray import SystemTray
from modules.weather import Weather
from widgets.wayland import WaylandWindow as Window

CHINESE_NUMERALS = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "〇"]

# Tooltips
tooltip_apps = f"""<b><u>Launcher</u></b>
<b>• Apps:</b> Type to search.

<b>• Calculator [Prefix "="]:</b> Solve a math expression.
  e.g. "=2+2"

<b>• Converter [Prefix ";"]:</b> Convert between units.
  e.g. ";100 USD to EUR", ";10 km to miles"

<b>• Special Commands [Prefix ":"]:</b>
  :update - Open {data.APP_NAME_CAP}'s updater.
  :d - Open Dashboard.
  :w - Open Wallpapers."""

tooltip_power = """<b>Power Menu</b>"""
tooltip_tools = """<b>Toolbox</b>"""
tooltip_overview = """<b>Overview</b>"""


class Bar(Window):
    def __init__(self, monitor_id: int = 0, **kwargs):
        self.monitor_id = monitor_id
        
        super().__init__(
            name="bar",
            layer="top",
            exclusivity="auto",
            visible=True,
            all_visible=True,
            monitor=monitor_id,
        )

        self.anchor_var = ""
        self.margin_var = ""

        match data.BAR_POSITION:
            case "Top":
                self.anchor_var = "left top right"
            case "Bottom":
                self.anchor_var = "left bottom right"
            case "Left":
                self.anchor_var = "left" if data.CENTERED_BAR else "left top bottom"
            case "Right":
                self.anchor_var = "right" if data.CENTERED_BAR else "top right bottom"
            case _:
                self.anchor_var = "left top right"

        if data.VERTICAL:
            match data.BAR_THEME:
                case "Edge":
                    self.margin_var = "-8px -8px -8px -8px"
                case _:
                    self.margin_var = "-4px -8px -4px -4px"
        else:
            match data.BAR_THEME:
                case "Edge":
                    self.margin_var = "-8px -8px -8px -8px"
                case _:
                    if data.BAR_POSITION == "Bottom":
                        self.margin_var = "-8px -4px -4px -4px"
                    else:
                        self.margin_var = "-4px -4px -8px -4px"

        self.set_anchor(self.anchor_var)
        self.set_margin(self.margin_var)

        self.notch = kwargs.get("notch", None)
        self.component_visibility = data.BAR_COMPONENTS_VISIBILITY

        self.dock_instance = None
        self.integrated_dock_widget = None

        # Calculate workspace range based on monitor_id
        # Monitor 0: workspaces 1-10, Monitor 1: workspaces 11-20, etc.
        start_workspace = self.monitor_id * 10 + 1
        end_workspace = start_workspace + 10
        workspace_range = range(start_workspace, end_workspace)

        self.workspaces = Workspaces(
            name="workspaces",
            invert_scroll=True,
            empty_scroll=True,
            v_align="fill",
            orientation="h" if not data.VERTICAL else "v",
            spacing=8,
            buttons=[
                WorkspaceButton(
                    h_expand=False,
                    v_expand=False,
                    h_align="center",
                    v_align="center",
                    id=i,
                    label=None,
                    style_classes=["vertical"] if data.VERTICAL else None,
                )
                for i in workspace_range
            ],
            buttons_factory=(
                None
                if data.BAR_HIDE_SPECIAL_WORKSPACE
                else Workspaces.default_buttons_factory
            ),
        )

        self.workspaces_num = Workspaces(
            name="workspaces-num",
            invert_scroll=True,
            empty_scroll=True,
            v_align="fill",
            orientation="h" if not data.VERTICAL else "v",
            spacing=0 if not data.BAR_WORKSPACE_USE_CHINESE_NUMERALS else 4,
            buttons=[
                WorkspaceButton(
                    h_expand=False,
                    v_expand=False,
                    h_align="center",
                    v_align="center",
                    id=i,
                    label=(
                        CHINESE_NUMERALS[(i - start_workspace)]
                        if data.BAR_WORKSPACE_USE_CHINESE_NUMERALS
                        and 0 <= (i - start_workspace) < len(CHINESE_NUMERALS)
                        else str(i)
                    ),
                )
                for i in workspace_range
            ],
            buttons_factory=(
                None
                if data.BAR_HIDE_SPECIAL_WORKSPACE
                else Workspaces.default_buttons_factory
            ),
        )

        self.ws_container = Box(
            name="workspaces-container",
            children=(
                self.workspaces
                if not data.BAR_WORKSPACE_SHOW_NUMBER
                else self.workspaces_num
            ),
        )

        self.button_tools = Button(
            name="button-bar",
            tooltip_markup=tooltip_tools,
            on_clicked=lambda *_: self.tools_menu(),
            child=Label(name="button-bar-label", markup=icons.toolbox),
        )

        self.connection = get_hyprland_connection()
        self.button_tools.connect("enter_notify_event", self.on_button_enter)
        self.button_tools.connect("leave_notify_event", self.on_button_leave)

        self.systray = SystemTray()

        self.weather = Weather()
        self.sysprofiles = Systemprofiles()

        self.network = NetworkApplet()

        self.lang_label = Label(name="lang-label")
        self.language = Button(
            name="language", h_align="center", v_align="center", child=self.lang_label
        )
        self.on_language_switch()
        self.connection.connect("event::activelayout", self.on_language_switch)

        # Determine date-time format based on the new setting
        if data.DATETIME_12H_FORMAT:
            time_format_horizontal = "%I:%M %p"
            time_format_vertical = "%I\n%M\n%p"
        else:
            time_format_horizontal = "%H:%M"
            time_format_vertical = "%H\n%M"

        self.date_time = DateTime(
            name="date-time",
            formatters=(
                [time_format_horizontal]
                if not data.VERTICAL
                else [time_format_vertical]
            ),
            h_align="center" if not data.VERTICAL else "fill",
            v_align="center",
            h_expand=True,
            v_expand=True,
            style_classes=["vertical"] if data.VERTICAL else [],
        )

        self.button_apps = Button(
            name="button-bar",
            tooltip_markup=tooltip_apps,
            on_clicked=lambda *_: self.search_apps(),
            child=Label(name="button-bar-label", markup=icons.apps),
        )
        self.button_apps.connect("enter_notify_event", self.on_button_enter)
        self.button_apps.connect("leave_notify_event", self.on_button_leave)

        self.button_power = Button(
            name="button-bar",
            tooltip_markup=tooltip_power,
            on_clicked=lambda *_: self.power_menu(),
            child=Label(name="button-bar-label", markup=icons.shutdown),
        )
        self.button_power.connect("enter_notify_event", self.on_button_enter)
        self.button_power.connect("leave_notify_event", self.on_button_leave)

        self.button_overview = Button(
            name="button-bar",
            tooltip_markup=tooltip_overview,
            on_clicked=lambda *_: self.overview(),
            child=Label(name="button-bar-label", markup=icons.windows),
        )
        self.button_overview.connect("enter_notify_event", self.on_button_enter)
        self.button_overview.connect("leave_notify_event", self.on_button_leave)

        self.control = ControlSmall()
        self.metrics = MetricsSmall()
        self.battery = Battery()

        self.apply_component_props()

        self.rev_right = [
            self.metrics,
            self.control,
        ]

        self.revealer_right = Revealer(
            name="bar-revealer",
            transition_type="slide-left",
            child_revealed=True,
            child=Box(
                name="bar-revealer-box",
                orientation="h",
                spacing=4,
                children=self.rev_right if not data.VERTICAL else None,
            ),
        )

        self.boxed_revealer_right = Box(
            name="boxed-revealer",
            children=[
                self.revealer_right,
            ],
        )

        self.rev_left = [
            self.weather,
            self.sysprofiles,
            self.network,
        ]

        self.revealer_left = Revealer(
            name="bar-revealer",
            transition_type="slide-right",
            child_revealed=True,
            child=Box(
                name="bar-revealer-box",
                orientation="h",
                spacing=4,
                children=self.rev_left if not data.VERTICAL else None,
            ),
        )

        self.boxed_revealer_left = Box(
            name="boxed-revealer",
            children=[
                self.revealer_left,
            ],
        )

        self.h_start_children = [
            self.button_apps,
            self.ws_container,
            self.button_overview,
            self.boxed_revealer_left,
        ]

        self.h_end_children = [
            self.boxed_revealer_right,
            self.battery,
            self.systray,
            self.button_tools,
            self.language,
            self.date_time,
            self.button_power,
        ]

        self.v_start_children = [
            self.button_apps,
            self.systray,
            self.control,
            self.sysprofiles,
            self.network,
            self.button_tools,
        ]

        self.v_center_children = [
            self.button_overview,
            self.ws_container,
            self.weather,
        ]

        self.v_end_children = [
            self.battery,
            self.metrics,
            self.language,
            self.date_time,
            self.button_power,
        ]

        self.v_all_children = []
        self.v_all_children.extend(self.v_start_children)
        self.v_all_children.extend(self.v_center_children)
        self.v_all_children.extend(self.v_end_children)

        # Create embedded dock when bar is in center position (regardless of DOCK_ENABLED setting)
        should_embed_dock = (
            data.BAR_POSITION == "Bottom"
            or (data.PANEL_THEME == "Panel" and data.BAR_POSITION in ["Top", "Bottom"])
        )
        
        if should_embed_dock:
            if not data.VERTICAL:
                self.dock_instance = Dock(integrated_mode=True)
                self.integrated_dock_widget = self.dock_instance.wrapper

        is_centered_bar = data.VERTICAL and getattr(data, "CENTERED_BAR", False)

        bar_center_actual_children = None

        if self.integrated_dock_widget is not None:
            bar_center_actual_children = self.integrated_dock_widget
        elif data.VERTICAL:
            bar_center_actual_children = Box(
                orientation=Gtk.Orientation.VERTICAL,
                spacing=4,
                children=(
                    self.v_all_children if is_centered_bar else self.v_center_children
                ),
            )

        self.bar_inner = CenterBox(
            name="bar-inner",
            orientation=(
                Gtk.Orientation.HORIZONTAL
                if not data.VERTICAL
                else Gtk.Orientation.VERTICAL
            ),
            h_align="fill",
            v_align="fill",
            start_children=(
                None
                if is_centered_bar
                else Box(
                    name="start-container",
                    spacing=4,
                    orientation=(
                        Gtk.Orientation.HORIZONTAL
                        if not data.VERTICAL
                        else Gtk.Orientation.VERTICAL
                    ),
                    children=(
                        self.h_start_children
                        if not data.VERTICAL
                        else self.v_start_children
                    ),
                )
            ),
            center_children=bar_center_actual_children,
            end_children=(
                None
                if is_centered_bar
                else Box(
                    name="end-container",
                    spacing=4,
                    orientation=(
                        Gtk.Orientation.HORIZONTAL
                        if not data.VERTICAL
                        else Gtk.Orientation.VERTICAL
                    ),
                    children=(
                        self.h_end_children
                        if not data.VERTICAL
                        else self.v_end_children
                    ),
                )
            ),
        )

        self.children = self.bar_inner

        self.hidden = False

        self.themed_children = [
            self.button_apps,
            self.button_overview,
            self.button_power,
            self.button_tools,
            self.language,
            self.date_time,
            self.ws_container,
            self.weather,
            self.network,
            self.battery,
            self.metrics,
            self.systray,
            self.control,
        ]
        if self.integrated_dock_widget:
            self.themed_children.append(self.integrated_dock_widget)

        current_theme = data.BAR_THEME
        theme_classes = ["pills", "dense", "edge", "edgecenter"]
        for tc in theme_classes:
            self.bar_inner.remove_style_class(tc)

        self.style = None
        match current_theme:
            case "Pills":
                self.style = "pills"
            case "Dense":
                self.style = "dense"
            case "Edge":
                if data.VERTICAL and data.CENTERED_BAR:
                    self.style = "edgecenter"
                else:
                    self.style = "edge"
            case _:
                self.style = "pills"

        self.bar_inner.add_style_class(self.style)

        if self.integrated_dock_widget and hasattr(
            self.integrated_dock_widget, "add_style_class"
        ):
            for theme_class_to_remove in ["pills", "dense", "edge"]:
                style_context = self.integrated_dock_widget.get_style_context()
                if style_context.has_class(theme_class_to_remove):
                    self.integrated_dock_widget.remove_style_class(
                        theme_class_to_remove
                    )
            self.integrated_dock_widget.add_style_class(self.style)

        if data.BAR_THEME == "Dense" or data.BAR_THEME == "Edge":
            for child in self.themed_children:
                if hasattr(child, "add_style_class"):
                    child.add_style_class("invert")

        match data.BAR_POSITION:
            case "Top":
                self.bar_inner.add_style_class("top")
            case "Bottom":
                self.bar_inner.add_style_class("bottom")
            case "Left":
                self.bar_inner.add_style_class("left")
            case "Right":
                self.bar_inner.add_style_class("right")
            case _:
                self.bar_inner.add_style_class("top")

        if data.VERTICAL:
            self.bar_inner.add_style_class("vertical")

        self.systray._update_visibility()
        self.chinese_numbers()

    def apply_component_props(self):
        components = {
            "button_apps": self.button_apps,
            "systray": self.systray,
            "control": self.control,
            "network": self.network,
            "button_tools": self.button_tools,
            "button_overview": self.button_overview,
            "ws_container": self.ws_container,
            "weather": self.weather,
            "battery": self.battery,
            "metrics": self.metrics,
            "language": self.language,
            "date_time": self.date_time,
            "button_power": self.button_power,
            "sysprofiles": self.sysprofiles,
        }

        for component_name, widget in components.items():
            if component_name in self.component_visibility:
                widget.set_visible(self.component_visibility[component_name])

    def toggle_component_visibility(self, component_name):
        components = {
            "button_apps": self.button_apps,
            "systray": self.systray,
            "control": self.control,
            "network": self.network,
            "button_tools": self.button_tools,
            "button_overview": self.button_overview,
            "ws_container": self.ws_container,
            "weather": self.weather,
            "battery": self.battery,
            "metrics": self.metrics,
            "language": self.language,
            "date_time": self.date_time,
            "button_power": self.button_power,
            "sysprofiles": self.sysprofiles,
        }

        if component_name in components and component_name in self.component_visibility:
            self.component_visibility[component_name] = not self.component_visibility[
                component_name
            ]
            components[component_name].set_visible(
                self.component_visibility[component_name]
            )

            config_file = os.path.expanduser(
                f"~/.config/{data.APP_NAME}/config/config.json"
            )
            if os.path.exists(config_file):
                try:
                    with open(config_file, "r") as f:
                        config = json.load(f)

                    config[f"bar_{component_name}_visible"] = self.component_visibility[
                        component_name
                    ]

                    with open(config_file, "w") as f:
                        json.dump(config, f, indent=4)
                except Exception as e:
                    print(f"Error updating config file: {e}")

            return self.component_visibility[component_name]

        return None

    def on_button_enter(self, widget, event):
        window = widget.get_window()
        if window:
            window.set_cursor(Gdk.Cursor.new_from_name(widget.get_display(), "hand2"))

    def on_button_leave(self, widget, event):
        window = widget.get_window()
        if window:
            window.set_cursor(None)

    def on_button_clicked(self, *args):
        exec_shell_command_async("notify-send 'Botón presionado' '¡Funciona!'")

    def search_apps(self):
        if self.notch:
            self.notch.open_notch("launcher")

    def overview(self):
        if self.notch:
            self.notch.open_notch("overview")

    def power_menu(self):
        if self.notch:
            self.notch.open_notch("power")

    def tools_menu(self):
        if self.notch:
            self.notch.open_notch("tools")

    def on_language_switch(self, _=None, event: HyprlandEvent = None):
        try:
            lang_data = (
                event.data[1]
                if event and event.data and len(event.data) > 1
                else Language().get_label()
            )
        except json.JSONDecodeError:
            lang_data = "UNK"  # Fallback to default language label
        self.language.set_tooltip_text(lang_data)
        if not data.VERTICAL:
            self.lang_label.set_label(lang_data[:3].upper())
        else:
            self.lang_label.add_style_class("icon")
            self.lang_label.set_markup(icons.keyboard)

    def toggle_hidden(self):
        self.hidden = not self.hidden
        if self.hidden:
            self.bar_inner.add_style_class("hidden")
        else:
            self.bar_inner.remove_style_class("hidden")
            # Ensure notch is above bar when bar is shown
            if self.notch:
                # Focus the notch window to bring it to front
                GLib.idle_add(lambda: exec_shell_command_async("hyprctl dispatch focuswindow class:notch") if self.notch else None)

    def chinese_numbers(self):
        if data.BAR_WORKSPACE_USE_CHINESE_NUMERALS:
            self.workspaces_num.add_style_class("chinese")
        else:
            self.workspaces_num.remove_style_class("chinese")
