#!/usr/bin/env python3
import json
import re
from collections import Counter
from pathlib import Path

ABBREVIATIONS = {
    "on-screen displays": ["osd"],
    "on-screen display": ["osd"],
    "do not disturb": ["dnd"],
    "keyboard shortcuts": ["keybinds", "hotkeys", "bindings", "keys"],
    "notifications": ["notif", "notifs", "alerts"],
    "notification": ["notif", "alert"],
    "wallpaper": ["background", "bg", "image", "picture", "desktop"],
    "transparency": ["opacity", "alpha", "translucent", "transparent"],
    "visibility": ["visible", "hide", "show", "hidden", "autohide", "auto-hide"],
    "temperature": ["temp", "celsius", "fahrenheit"],
    "configuration": ["config", "configure", "setup"],
    "applications": ["apps", "programs"],
    "application": ["app", "program"],
    "animation": ["motion", "transition", "animate", "animations"],
    "typography": ["font", "fonts", "text", "typeface"],
    "workspaces": ["workspace", "desktops", "virtual"],
    "workspace": ["desktop", "virtual"],
    "bluetooth": ["bt"],
    "network": ["wifi", "wi-fi", "ethernet", "internet", "connection", "wireless"],
    "display": ["monitor", "screen", "output"],
    "displays": ["monitors", "screens", "outputs"],
    "brightness": ["bright", "dim", "backlight"],
    "volume": ["audio", "sound", "speaker", "loudness"],
    "battery": ["power", "charge", "charging"],
    "clock": ["time", "watch"],
    "calendar": ["date", "day", "month", "year"],
    "launcher": ["app drawer", "app menu", "start menu", "applications"],
    "dock": ["taskbar", "panel"],
    "bar": ["panel", "taskbar", "topbar", "statusbar"],
    "theme": ["appearance", "look", "style", "colors", "colour"],
    "color": ["colour", "hue", "tint"],
    "colors": ["colours", "palette"],
    "dark": ["night", "dark mode"],
    "light": ["day", "light mode"],
    "lock screen": ["lockscreen", "login", "security"],
    "power": ["shutdown", "reboot", "restart", "suspend", "hibernate", "sleep"],
    "idle": ["afk", "inactive", "timeout", "screensaver"],
    "gamma": ["color temperature", "night light", "blue light", "redshift"],
    "media player": ["mpris", "music", "audio", "playback"],
    "clipboard": ["copy", "paste", "cliphist", "history"],
    "updater": ["updates", "upgrade", "packages"],
    "plugins": ["extensions", "addons", "widgets"],
    "spacing": ["gap", "gaps", "margin", "margins", "padding"],
    "corner": ["corners", "rounded", "radius", "round"],
    "matugen": ["dynamic", "wallpaper colors", "material"],
    "running apps": ["taskbar", "windows", "active", "open"],
    "weather": ["forecast", "temperature", "climate"],
    "sounds": ["audio", "effects", "sfx"],
    "printers": ["print", "cups", "printing"],
    "widgets": ["components", "modules"],
}

CATEGORY_KEYWORDS = {
    "Personalization": ["customize", "custom", "personal", "appearance"],
    "Time & Weather": ["clock", "forecast", "date"],
    "Keyboard Shortcuts": ["keys", "bindings", "hotkey"],
    "Dank Bar": ["panel", "topbar", "statusbar"],
    "Applications": ["apps", "programs", "window", "rules", "matching", "floating"],
    "Dock": ["taskbar", "launcher bar"],
    "Network": ["connectivity", "online"],
    "System": ["os", "linux"],
    "Launcher": ["start", "menu", "drawer"],
    "Theme & Colors": ["appearance", "look", "style", "scheme"],
    "Lock Screen": ["security", "login", "password"],
    "Plugins": ["extend", "addon"],
    "About": ["info", "version", "credits", "help"],
    "Typography & Motion": ["fonts", "animation", "text"],
    "Sounds": ["audio", "sfx", "effects"],
    "Media Player": ["music", "spotify", "mpris"],
    "Notifications": ["alerts", "messages", "toast"],
    "On-screen Displays": ["osd", "indicator", "popup"],
    "Running Apps": ["windows", "tasks", "active"],
    "System Updater": ["packages", "upgrade"],
    "Power & Sleep": ["shutdown", "suspend", "energy"],
    "Displays": ["monitor", "screen", "resolution"],
    "Desktop Widgets": ["conky", "desktop clock"],
    "Audio": ["sound", "volume", "speaker", "microphone", "headphones", "pipewire"],
    "Locale": ["locale", "language", "country"],
    "Greeter": ["login", "greetd", "display manager"],
    "Multiplexers": ["tmux", "zellij", "terminal"],
    "Frame": ["window", "border", "decoration"],
    "Default Apps": ["browser", "terminal", "handlers", "mime"],
    "Users": ["accounts", "user", "profile"],
    "Autostart": ["startup", "launch", "boot"],
}

TAB_INDEX_MAP = {
    "WallpaperTab.qml": 0,
    "TimeWeatherTab.qml": 1,
    "KeybindsTab.qml": 2,
    "DankBarTab.qml": 3,
    "WorkspacesTab.qml": 4,
    "CompositorLayoutTab.qml": 37,
    "WindowRulesTab.qml": 38,
    "DockTab.qml": 5,
    "DankBarAppearanceTab.qml": 6,
    "WorkspaceAppearanceCard.qml": 4,
    "NetworkStatusTab.qml": 7,
    "NetworkEthernetTab.qml": 39,
    "NetworkWifiTab.qml": 40,
    "NetworkVpnTab.qml": 41,
    "PrinterTab.qml": 8,
    "LauncherTab.qml": 9,
    "ThemeColorsTab.qml": 10,
    "LockScreenTab.qml": 11,
    "PluginsTab.qml": 12,
    "AboutTab.qml": 13,
    "TypographyMotionTab.qml": 14,
    "SoundsTab.qml": 15,
    "MediaPlayerTab.qml": 16,
    "NotificationsTab.qml": 17,
    "OSDTab.qml": 18,
    "RunningAppsTab.qml": 19,
    "SystemUpdaterTab.qml": 20,
    "PowerSleepTab.qml": 21,
    "WidgetsTab.qml": 22,
    "ClipboardTab.qml": 23,
    "DisplayConfigTab.qml": 24,
    "GammaControlTab.qml": 25,
    "DisplayWidgetsTab.qml": 26,
    "DesktopWidgetsTab.qml": 27,
    "AudioTab.qml": 29,
    "LocaleTab.qml": 30,
    "GreeterTab.qml": 31,
    "MuxTab.qml": 32,
    "FrameTab.qml": 33,
    "DefaultAppsTab.qml": 34,
    "UsersTab.qml": 35,
    "AutoStartTab.qml": 36,
    "BatteryTab.qml": 42,
    "MouseTouchpadTab.qml": 44,
    "KeyboardTab.qml": 45,
}

FILE_CONDITION_MAP = {
    "GreeterTab.qml": "greeterAvailable",
    "MouseTouchpadTab.qml": "isNiri",
    "KeyboardTab.qml": "isNiri",
}

TAB_CATEGORY_MAP = {
    0: "Personalization",
    1: "Time & Weather",
    2: "Keyboard Shortcuts",
    3: "Dank Bar",
    4: "Dank Bar",
    5: "Dock",
    6: "Dank Bar",
    7: "Network",
    8: "System",
    9: "Launcher",
    10: "Theme & Colors",
    11: "Lock Screen",
    12: "Plugins",
    13: "About",
    14: "Typography & Motion",
    15: "Sounds",
    16: "Media Player",
    17: "Notifications",
    18: "On-screen Displays",
    19: "Running Apps",
    20: "System Updater",
    21: "Power & Sleep",
    22: "Dank Bar",
    23: "System",
    24: "Displays",
    25: "Displays",
    26: "Displays",
    27: "Desktop Widgets",
    29: "Audio",
    30: "Locale",
    31: "Greeter",
    32: "Multiplexers",
    33: "Frame",
    34: "Default Apps",
    35: "Users",
    36: "Autostart",
    37: "Personalization",
    38: "Applications",
    39: "Network",
    40: "Network",
    41: "Network",
    42: "Power & Security",
    43: "Dank Dash",
    44: "System",
    45: "System",
}

SEARCHABLE_COMPONENTS = [
    "SettingsCard",
    "SettingsToggleRow",
    "SettingsSliderCard",
    "SettingsDropdownRow",
    "SettingsButtonGroupRow",
    "SettingsSliderRow",
    "SettingsToggleCard",
]

STOPWORDS = {
    "the",
    "and",
    "for",
    "with",
    "from",
    "this",
    "that",
    "are",
    "was",
    "will",
    "can",
    "has",
    "have",
    "been",
    "when",
    "your",
    "use",
    "used",
    "using",
    "instead",
    "like",
    "such",
    "also",
    "only",
    "which",
    "each",
    "other",
    "some",
    "into",
    "than",
    "then",
    "them",
    "these",
    "those",
}


def enrich_keywords(label, description, category, existing_tags):
    keywords = set(existing_tags)

    label_lower = label.lower()
    label_words = re.split(r"[\s\-_&/]+", label_lower)
    keywords.update(w for w in label_words if len(w) > 2)

    for term, aliases in ABBREVIATIONS.items():
        if term in label_lower:
            keywords.update(aliases)

    if description:
        desc_lower = description.lower()
        desc_words = re.split(r"[\s\-_&/,.]+", desc_lower)
        keywords.update(w for w in desc_words if len(w) > 3 and w.isalpha())
        for term, aliases in ABBREVIATIONS.items():
            if term in desc_lower:
                keywords.update(aliases)

    if category in CATEGORY_KEYWORDS:
        keywords.update(CATEGORY_KEYWORDS[category])

    cat_lower = category.lower()
    cat_words = re.split(r"[\s\-_&/]+", cat_lower)
    keywords.update(w for w in cat_words if len(w) > 2)

    keywords = {k for k in keywords if k not in STOPWORDS and len(k) > 1}
    return sorted(keywords)


def extract_i18n_string(value):
    match = re.search(r'I18n\.tr\(["\']([^"\']+)["\']', value)
    if match:
        return match.group(1)
    match = re.search(r'^["\']([^"\']+)["\']$', value.strip())
    if match:
        return match.group(1)
    return None


def extract_tags(value):
    match = re.search(r"\[([^\]]+)\]", value)
    if not match:
        return []
    content = match.group(1)
    tags = re.findall(r'["\']([^"\']+)["\']', content)
    return tags


def parse_component_block(content, start_pos, component_name):
    brace_count = 0
    started = False
    block_start = start_pos

    for i in range(start_pos, len(content)):
        if content[i] == "{":
            if not started:
                block_start = i
                started = True
            brace_count += 1
        elif content[i] == "}":
            brace_count -= 1
            if started and brace_count == 0:
                return content[block_start : i + 1]
    return ""


def extract_property(block, prop_name):
    pattern = rf"{prop_name}\s*:\s*([^\n]+)"
    match = re.search(pattern, block)
    if match:
        return match.group(1).strip()
    return None


def load_wrapper_components(root_dir):
    widgets_dir = Path(root_dir) / "Modules" / "Settings" / "Widgets"
    wrappers = {}

    for qml_file in sorted(widgets_dir.glob("*.qml")):
        if qml_file.stem in SEARCHABLE_COMPONENTS:
            continue

        with open(qml_file, "r", encoding="utf-8") as f:
            content = f.read()

        root_match = re.search(r"^(\w+)\s*\{", content, re.MULTILINE)
        if not root_match or root_match.group(1) not in SEARCHABLE_COMPONENTS:
            continue

        wrappers[qml_file.stem] = {
            prop: extract_property(content, prop)
            for prop in ("settingKey", "title", "text", "description", "iconName", "tags")
        }

    return wrappers


def find_settings_components(content, filename, wrappers):
    results = []
    file_tab_index = TAB_INDEX_MAP.get(filename, -1)

    if file_tab_index == -1:
        return results

    for component in SEARCHABLE_COMPONENTS + sorted(wrappers):
        defaults = wrappers.get(component, {})
        pattern = rf"\b{component}\s*\{{"
        for match in re.finditer(pattern, content):
            block = parse_component_block(content, match.start(), component)
            if not block:
                continue

            setting_key = extract_property(block, "settingKey") or defaults.get("settingKey")
            if setting_key:
                setting_key = setting_key.strip("\"'")

            if not setting_key:
                continue

            tab_index = file_tab_index
            tab_raw = extract_property(block, "tab")
            if tab_raw and tab_raw.strip("\"'") == "appearance":
                tab_index = 6

            title_raw = extract_property(block, "title") or defaults.get("title")
            text_raw = extract_property(block, "text") or defaults.get("text")
            label = None
            if title_raw:
                label = extract_i18n_string(title_raw)
            if not label and text_raw:
                label = extract_i18n_string(text_raw)

            if not label:
                continue

            icon_raw = extract_property(block, "iconName") or defaults.get("iconName")
            icon = None
            if icon_raw:
                icon = icon_raw.strip("\"'")
                if icon.startswith("{") or "?" in icon:
                    icon = None

            tags_raw = extract_property(block, "tags") or defaults.get("tags")
            tags = []
            if tags_raw:
                tags = extract_tags(tags_raw)

            desc_raw = extract_property(block, "description") or defaults.get("description")
            description = None
            if desc_raw:
                description = extract_i18n_string(desc_raw)

            visible_raw = extract_property(block, "visible")
            condition_key = FILE_CONDITION_MAP.get(filename)
            if visible_raw:
                if all(c in visible_raw for c in ("CompositorService.isNiri", "CompositorService.isHyprland", "CompositorService.isMango")):
                    condition_key = "windowRulesCapable"
                elif "CompositorService.isNiri" in visible_raw:
                    condition_key = "isNiri"
                elif "CompositorService.isHyprland" in visible_raw:
                    condition_key = "isHyprland"
                elif "CompositorService.isMango" in visible_raw:
                    condition_key = "isMango"
                elif "KeybindsService.available" in visible_raw:
                    condition_key = "keybindsAvailable"
                elif "AudioService.soundsAvailable" in visible_raw:
                    condition_key = "soundsAvailable"
                elif "CupsService.cupsAvailable" in visible_raw:
                    condition_key = "cupsAvailable"
                elif "NetworkService.networkAvailable" in visible_raw:
                    condition_key = "networkAvailable"
                elif "DMSService.isConnected" in visible_raw:
                    condition_key = "dmsConnected"
                elif "Theme.matugenAvailable" in visible_raw:
                    condition_key = "matugenAvailable"
                elif "CompositorService.isDwl" in visible_raw:
                    condition_key = "isDwl"

            category = TAB_CATEGORY_MAP.get(tab_index, "Settings")
            enriched_keywords = enrich_keywords(label, description, category, tags)

            entry = {
                "section": setting_key,
                "label": label,
                "tabIndex": tab_index,
                "category": category,
                "keywords": enriched_keywords,
            }

            if icon:
                entry["icon"] = icon
            if description:
                entry["description"] = description
            if condition_key:
                entry["conditionKey"] = condition_key

            results.append(entry)

    return results


def parse_tabs_from_sidebar(sidebar_file):
    with open(sidebar_file, "r", encoding="utf-8") as f:
        content = f.read()

    pattern = r'"text"\s*:\s*I18n\.tr\("([^"]+)"(?:,\s*"[^"]+"(?:,\s*true)?)?\).*?"icon"\s*:\s*"([^"]+)".*?"tabIndex"\s*:\s*(\d+)'
    tabs = []

    for match in re.finditer(pattern, content, re.DOTALL):
        label, icon, tab_idx = match.group(1), match.group(2), int(match.group(3))

        before_text = content[: match.start()]
        parent_match = re.search(
            r'"text"\s*:\s*I18n\.tr\("([^"]+)"\)[^{]*"children"[^[]*\[[^{]*$',
            before_text,
        )
        parent = parent_match.group(1) if parent_match else None

        cond = None
        after_pos = match.end()
        snippet = content[match.start() : min(after_pos + 200, len(content))]
        for qml_cond, key in [
            ("shortcutsOnly", "keybindsAvailable"),
            ("soundsOnly", "soundsAvailable"),
            ("cupsOnly", "cupsAvailable"),
            ("greeterOnly", "greeterAvailable"),
            ("dmsOnly", "dmsConnected"),
            ("hyprlandNiriOnly", "isHyprlandOrNiri"),
            ("clipboardOnly", "dmsConnected"),
            ("niriOnly", "isNiri"),
            ("windowRulesCapable", "windowRulesCapable"),
            ("layoutCapable", "layoutCapable"),
        ]:
            if f'"{qml_cond}": true' in snippet:
                cond = key
                break

        tabs.append(
            {
                "tabIndex": tab_idx,
                "label": label,
                "icon": icon,
                "parent": parent,
                "conditionKey": cond,
            }
        )

    return tabs


def generate_tab_entries(sidebar_file, settings_entries=None):
    tabs = parse_tabs_from_sidebar(sidebar_file)
    settings_entries = settings_entries or []
    highlightable_labels = {
        (entry["tabIndex"], entry["label"])
        for entry in settings_entries
        if not str(entry["section"]).startswith("_tab_")
    }

    label_counts = Counter([t["label"] for t in tabs])

    entries = []
    for tab in tabs:
        label = (
            f"{tab['parent']}: {tab['label']}"
            if label_counts[tab["label"]] > 1 and tab["parent"]
            else tab["label"]
        )
        category = TAB_CATEGORY_MAP.get(tab["tabIndex"], "Settings")

        if (tab["tabIndex"], label) in highlightable_labels:
            continue

        keywords = enrich_keywords(tab["label"], None, category, [])

        if tab["parent"]:
            parent_keywords = [
                w for w in re.split(r"[\s\-_&/]+", tab["parent"].lower()) if len(w) > 2
            ]
            keywords = sorted(
                set(
                    keywords
                    + parent_keywords
                    + [k for p in parent_keywords for k in ABBREVIATIONS.get(p, [])]
                )
            )

        entry = {
            "section": f"_tab_{tab['tabIndex']}",
            "label": label,
            "tabIndex": tab["tabIndex"],
            "category": category,
            "keywords": keywords,
            "icon": tab["icon"],
        }
        if tab["conditionKey"]:
            entry["conditionKey"] = tab["conditionKey"]
        entries.append(entry)

    return entries


def extract_settings_index(root_dir):
    settings_dir = Path(root_dir) / "Modules" / "Settings"
    wrappers = load_wrapper_components(root_dir)
    all_entries = []
    seen_keys = set()

    for qml_file in sorted(settings_dir.glob("*.qml")):
        if qml_file.name not in TAB_INDEX_MAP:
            continue

        with open(qml_file, "r", encoding="utf-8") as f:
            content = f.read()

        entries = find_settings_components(content, qml_file.name, wrappers)
        for entry in entries:
            key = entry["section"]
            if key not in seen_keys:
                seen_keys.add(key)
                all_entries.append(entry)

    if "windowRules" not in seen_keys:
        all_entries.append(
            {
                "section": "windowRules",
                "label": "Window Rules",
                "tabIndex": 38,
                "category": "Applications",
                "keywords": enrich_keywords(
                    "Window Rules",
                    "Define compositor rules for window behavior",
                    "Applications",
                    ["matching", "floating", "fullscreen", "opacity"],
                ),
                "icon": "select_window",
                "description": "Define compositor rules for window behavior",
                "conditionKey": "windowRulesCapable",
            }
        )

    return all_entries


def main():
    script_dir = Path(__file__).parent
    root_dir = script_dir.parent
    sidebar_file = root_dir / "Modals" / "Settings" / "SettingsSidebar.qml"

    print("Extracting settings search index...")
    settings_entries = extract_settings_index(root_dir)
    tab_entries = generate_tab_entries(sidebar_file, settings_entries)

    all_entries = tab_entries + settings_entries

    all_entries.sort(key=lambda x: (x["tabIndex"], x["label"], x["section"]))

    output_path = script_dir / "settings_search_index.json"
    with open(output_path, "w", encoding="utf-8") as f:
        json.dump(all_entries, f, indent=2, ensure_ascii=False)
        f.write("\n")

    print(f"Found {len(settings_entries)} searchable settings")
    print(f"Found {len(tab_entries)} tab entries")
    print(f"Total: {len(all_entries)} entries")
    print(f"Output: {output_path}")

    conditions = set()
    for entry in all_entries:
        if "conditionKey" in entry:
            conditions.add(entry["conditionKey"])

    if conditions:
        print(f"Condition keys found: {', '.join(sorted(conditions))}")


if __name__ == "__main__":
    main()
