from pathlib import Path
import subprocess
import ctypes
import gradio as gr
from pages import load, cache_dir

config = load()
cache_dir = cache_dir()
info_dir = Path(cache_dir) / "vscode_plugin_info"
info_dir.mkdir(parents=True, exist_ok=True)


_installed_extensions_cache = None


def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except Exception:
        return False


def flush_extension_cache():
    """
    Flushes the cached installed extensions.
    This is useful if you want to force a re-check of installed extensions.
    """
    global _installed_extensions_cache
    _installed_extensions_cache = None


def is_extension_installed(extension_id):
    """
    Checks if a VS Code extension is installed (cached).
    Args:
        extension_id (str): The extension identifier (publisher.extension).
    Returns:
        bool: True if installed, False otherwise.
    """
    global _installed_extensions_cache
    if _installed_extensions_cache is None:
        try:
            result = subprocess.run(
                ["code.cmd", "--list-extensions"],
                capture_output=True,
                text=True,
                check=True,
            )
            _installed_extensions_cache = set(result.stdout.splitlines())
        except subprocess.CalledProcessError as e:
            raise gr.Error(f"Failed to check installed extensions: {e.stderr}")
    return (
        extension_id.lower() in _installed_extensions_cache
        or extension_id in _installed_extensions_cache
    )


def install_extension(extension_id):
    """
    Installs the VS Code extension using the VS Code CLI.
    Args:
        extension_id (str): Extension identifier to install.
    """
    try:
        result = subprocess.run(
            ["code.cmd", "--install-extension", extension_id],
            capture_output=True,
            text=True,
            check=True,
        )
    except subprocess.CalledProcessError as e:
        gr.Warning(f"Failed to install {extension_id}: {e.stderr}")


def uninstall_extension(extension_id):
    """
    Uninstalls the VS Code extension using the VS Code CLI.
    Args:
        extension_id (str): Extension identifier to uninstall.
    """
    try:
        result = subprocess.run(
            ["code.cmd", "--uninstall-extension", extension_id],
            capture_output=True,
            text=True,
            check=True,
        )
    except subprocess.CalledProcessError as e:
        gr.Warning(f"Failed to uninstall {extension_id}: {e.stderr}")


def is_developer_mode_enabled():
    """
    Checks if Windows Developer Mode is enabled.
    Returns:
        bool: True if enabled, False otherwise.
    """
    try:
        # Query registry for Developer Mode status
        result = subprocess.run(
            [
                "reg",
                "query",
                r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock",
                "/v",
                "AllowDevelopmentWithoutDevLicense",
            ],
            capture_output=True,
            text=True,
            check=True,
        )
        return "0x1" in result.stdout
    except Exception:
        return False


def set_developer_mode(enabled: bool):
    """
    Enables or disables Windows Developer Mode.
    Args:
        enabled (bool): True to enable, False to disable.
    """
    value = "1" if enabled else "0"
    try:
        subprocess.run(
            [
                "reg",
                "add",
                r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock",
                "/v",
                "AllowDevelopmentWithoutDevLicense",
                "/t",
                "REG_DWORD",
                "/d",
                value,
                "/f",
            ],
            capture_output=True,
            text=True,
            check=True,
        )
        return True
    except Exception as e:
        if is_admin():
            raise gr.Error(f"Failed to set Developer Mode: {e}")
        else:
            gr.Warning(
                "Administrator privileges required. Please run this app as administrator."
            )
            return False


def configure_vscode_extensions(data):
    """
    Creates the VS Code Extensions configuration interface.
    Args:
        data: Dictionary of categorized extension IDs to configure.
    """
    # Extract all extension IDs from the categorized structure
    all_extension_ids = []
    categories = {}

    for category_name, extensions in data.items():
        if isinstance(extensions, list):
            # This is a list of extensions
            all_extension_ids.extend(extensions)
            categories[category_name] = extensions

    # Create accordion-based interface for each category
    category_checkboxes = {}

    for category_name, extensions in categories.items():
        with gr.Accordion(
            label=f"{category_name} ({len(extensions)} extensions)", open=False
        ):
            category_checkboxes[category_name] = gr.CheckboxGroup(
                choices=extensions,
                value=[ext for ext in extensions if is_extension_installed(ext)],
                label="",
                elem_id=f"checkbox_group_{category_name.replace(' ', '_').lower()}",
                interactive=True,
            )

    with gr.Row():
        select_all_btn = gr.Button("Select All", elem_id="select_all_btn")
        unselect_all_btn = gr.Button("Unselect All", elem_id="unselect_all_btn")
        reset_btn = gr.Button("Reset", elem_id="reset_btn")

    install_list = gr.Dropdown(
        choices=[],
        multiselect=True,
        label="Extensions to Install",
        elem_id="install_list_box",
        allow_custom_value=True,
        interactive=False,
    )
    uninstall_list = gr.Dropdown(
        choices=[],
        multiselect=True,
        label="Extensions to Uninstall",
        elem_id="uninstall_list_box",
        allow_custom_value=True,
        interactive=False,
    )
    submit_btn = gr.Button("Install / Uninstall", elem_id="submit_btn")

    def get_all_selected():
        """Get all currently selected extensions from all categories"""
        selected = []
        for checkbox in category_checkboxes.values():
            if hasattr(checkbox, "value") and checkbox.value:
                selected.extend(checkbox.value)
        return selected

    def on_category_change(*selected_by_category):
        """Handle changes in any category checkbox group"""
        all_selected = []
        for selected_list in selected_by_category:
            if selected_list:
                all_selected.extend(selected_list)

        to_install = [v for v in all_selected if not is_extension_installed(v)]
        to_uninstall = [
            v
            for v in all_extension_ids
            if is_extension_installed(v) and v not in all_selected
        ]

        return to_install, to_uninstall

    def select_all():
        """Select all extensions in all categories"""
        return [categories[cat] for cat in categories.keys()]

    def unselect_all():
        """Unselect all extensions in all categories"""
        return [[] for _ in categories.keys()]

    def reset():
        """Reset to currently installed extensions"""
        flush_extension_cache()
        reset_values = []
        for category_name, extensions in categories.items():
            installed_in_category = [
                ext for ext in extensions if is_extension_installed(ext)
            ]
            reset_values.append(installed_in_category)
        return (*reset_values, [], [])

    def submit(_install_list, _uninstall_list, progress=gr.Progress()):
        if not _install_list and not _uninstall_list:
            gr.Warning("No extensions selected for installation or uninstallation.")
            return

        total_steps = len(_install_list or []) + len(_uninstall_list or []) + 1
        steps_completed = 0

        for ext_id in _install_list or []:
            steps_completed += 1
            progress((steps_completed, total_steps), desc=f"Installing {ext_id}")
            install_extension(ext_id)

        for ext_id in _uninstall_list or []:
            steps_completed += 1
            progress((steps_completed, total_steps), desc=f"Uninstalling {ext_id}")
            uninstall_extension(ext_id)

        progress((steps_completed, total_steps), desc="Finalizing...")
        flush_extension_cache()

        # Return updated states for each category
        reset_values = []
        for category_name, extensions in categories.items():
            installed_in_category = [
                ext for ext in extensions if is_extension_installed(ext)
            ]
            reset_values.append(installed_in_category)
        return (*reset_values, [], [])

    # Wire up event handlers for all category checkboxes
    checkbox_list = list(category_checkboxes.values())

    for checkbox in checkbox_list:
        checkbox.change(
            on_category_change,
            inputs=checkbox_list,
            outputs=[install_list, uninstall_list],
        )

    select_all_btn.click(select_all, inputs=[], outputs=checkbox_list)

    unselect_all_btn.click(unselect_all, inputs=[], outputs=checkbox_list)

    reset_btn.click(
        reset, inputs=[], outputs=checkbox_list + [install_list, uninstall_list]
    )

    submit_btn.click(
        submit,
        inputs=[install_list, uninstall_list],
        outputs=checkbox_list + [install_list, uninstall_list],
    )


def configure_windows_developer_mode(data):
    """
    Creates the Windows Developer Mode configuration interface.
    Args:
        data: Configuration data for Windows Developer Mode settings.
    """
    # Developer Mode Section
    with gr.Accordion(label="Developer Mode Settings", open=False):
        gr.Markdown(
            """
        ### Developer Mode Configuration

        You can configure settings like:
        - Developer Mode.
        - File Explorer settings.
            - Show file extensions
            - Show hidden and system files
        - Terminal: Set Windows Terminal as your default terminal application.

        **Important:** Enabling Developer Mode may make your system less secure. Only enable if you understand the implications.
        """
        )

        with gr.Row():
            open_dev_mode_settings_btn = gr.Button(
                "Open Developer Mode Settings", elem_id="open_dev_mode_settings_btn"
            )

    def open_developer_settings():
        """Opens the Windows Developer Mode settings page."""
        try:
            subprocess.run(["start", "ms-settings:developers"], shell=True, check=True)
            gr.Info("Developer Mode settings opened successfully!")
        except Exception as e:
            gr.Warning(f"Failed to open Developer Mode settings: {e}")

    open_dev_mode_settings_btn.click(open_developer_settings, inputs=[], outputs=[])


def get_configuration_handler(configuration_name):
    """
    Returns the appropriate configuration handler function for the given configuration name.
    Args:
        configuration_name (str): Name of the configuration.
    Returns:
        function: Handler function for the configuration.
    """
    handlers = {
        "VS Code Extensions": configure_vscode_extensions,
        "Windows Developer Mode": configure_windows_developer_mode,
    }
    return handlers.get(configuration_name)


def get_interface(container):
    """
    Renders a basic Gradio UI for the configure app.
    Args:
        container: Gradio container/context.
    """
    with container:
        with gr.Row():
            gr.Markdown(f"# {config.title}")

        gr.Markdown(config.description)

        configurations = config.config["configure"]
        for configuration, data in configurations.items():

            print(f"Configuring {configuration}...")
            print(f"Data: {data}")

            if data is None:
                label = f"{configuration} (nothing to configure ...)"
                with gr.Accordion(label=label, open=False):
                    gr.Markdown("No further configuration found.")
                continue

            with gr.Accordion(label=configuration, open=False):
                handler = get_configuration_handler(configuration)
                if handler:
                    handler(data)
                else:
                    gr.Markdown(f"No handler found for configuration: {configuration}")
