use crate::common::requirements::{InstallTest, RequiredPackage};
use crate::ui::prelude::NerdFont;

use super::store::{BoolSettingKey, StringSettingKey};
use super::users;

/// A requirement that must be satisfied before a setting can be used
#[derive(Debug, Clone)]
pub enum SettingRequirement {
    /// Requires a package to be installed
    Package(RequiredPackage),
    /// Requires a custom condition to be true (e.g., service running, hardware present)
    Condition {
        description: &'static str,
        check: fn() -> bool,
        resolve_hint: &'static str,
    },
}

impl SettingRequirement {
    /// Check if this requirement is currently satisfied
    pub fn is_satisfied(&self) -> bool {
        match self {
            SettingRequirement::Package(pkg) => pkg.is_installed(),
            SettingRequirement::Condition { check, .. } => check(),
        }
    }

    /// Get a human-readable description of this requirement
    pub fn description(&self) -> &str {
        match self {
            SettingRequirement::Package(pkg) => pkg.name,
            SettingRequirement::Condition { description, .. } => description,
        }
    }

    /// Get a hint for how to resolve this requirement
    pub fn resolve_hint(&self) -> String {
        match self {
            SettingRequirement::Package(pkg) => pkg.install_hint(),
            SettingRequirement::Condition { resolve_hint, .. } => resolve_hint.to_string(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct SettingCategory {
    pub id: &'static str,
    pub title: &'static str,
    pub description: &'static str,
    pub icon: NerdFont,
}

#[derive(Debug, Clone, Copy)]
pub struct SettingOption {
    pub value: &'static str,
    pub label: &'static str,
    pub description: &'static str,
}

#[derive(Debug, Clone)]
pub enum SettingKind {
    Toggle {
        key: BoolSettingKey,
        summary: &'static str,
        apply: Option<fn(&mut super::SettingsContext, bool) -> anyhow::Result<()>>,
    },
    Choice {
        key: StringSettingKey,
        summary: &'static str,
        options: &'static [SettingOption],
        apply: Option<fn(&mut super::SettingsContext, &SettingOption) -> anyhow::Result<()>>,
    },
    Action {
        summary: &'static str,
        run: fn(&mut super::SettingsContext) -> anyhow::Result<()>,
    },
    Command {
        summary: &'static str,
        command: CommandSpec,
    },
}

#[derive(Debug, Clone)]
pub struct SettingDefinition {
    pub id: &'static str,
    pub title: &'static str,
    pub category: &'static str,
    pub icon: NerdFont,
    pub breadcrumbs: &'static [&'static str],
    pub kind: SettingKind,
    pub requires_reapply: bool,
    /// Requirements that must be satisfied before this setting can be used
    pub requirements: &'static [SettingRequirement],
}

#[derive(Debug, Clone, Copy)]
pub enum CommandStyle {
    Terminal,
    Detached,
}

#[derive(Debug, Clone, Copy)]
pub struct CommandSpec {
    pub program: &'static str,
    pub args: &'static [&'static str],
    pub style: CommandStyle,
}

impl CommandSpec {
    pub const fn terminal(program: &'static str, args: &'static [&'static str]) -> Self {
        Self {
            program,
            args,
            style: CommandStyle::Terminal,
        }
    }

    pub const fn detached(program: &'static str, args: &'static [&'static str]) -> Self {
        Self {
            program,
            args,
            style: CommandStyle::Detached,
        }
    }
}

const WIREMIX_PACKAGE: RequiredPackage = RequiredPackage {
    name: "wiremix",
    arch_package_name: Some("wiremix"),
    ubuntu_package_name: None,
    tests: &[InstallTest::WhichSucceeds("wiremix")],
};

pub const UDISKIE_PACKAGE: RequiredPackage = RequiredPackage {
    name: "udiskie",
    arch_package_name: Some("udiskie"),
    ubuntu_package_name: Some("udiskie"),
    tests: &[InstallTest::WhichSucceeds("udiskie")],
};

pub const GNOME_DISKS_PACKAGE: RequiredPackage = RequiredPackage {
    name: "GNOME Disks",
    arch_package_name: Some("gnome-disk-utility"),
    ubuntu_package_name: Some("gnome-disk-utility"),
    tests: &[InstallTest::WhichSucceeds("gnome-disks")],
};

pub const GPARTED_PACKAGE: RequiredPackage = RequiredPackage {
    name: "GParted",
    arch_package_name: Some("gparted"),
    ubuntu_package_name: Some("gparted"),
    tests: &[InstallTest::WhichSucceeds("gparted")],
};

pub const FASTFETCH_PACKAGE: RequiredPackage = RequiredPackage {
    name: "fastfetch",
    arch_package_name: Some("fastfetch"),
    ubuntu_package_name: Some("fastfetch"),
    tests: &[InstallTest::WhichSucceeds("fastfetch")],
};

pub const TOPGRADE_PACKAGE: RequiredPackage = RequiredPackage {
    name: "topgrade",
    arch_package_name: Some("topgrade"),
    ubuntu_package_name: None,
    tests: &[InstallTest::WhichSucceeds("topgrade")],
};

pub const BLUETOOTH_SERVICE_KEY: BoolSettingKey = BoolSettingKey::new("bluetooth.service", false);
pub const BLUETOOTH_HARDWARE_OVERRIDE_KEY: BoolSettingKey =
    BoolSettingKey::new("bluetooth.hardware_override", false);

pub const UDISKIE_AUTOMOUNT_KEY: BoolSettingKey = BoolSettingKey::new("storage.udiskie", false);

pub const BLUEZ_PACKAGE: RequiredPackage = RequiredPackage {
    name: "BlueZ bluetooth daemon",
    arch_package_name: Some("bluez"),
    ubuntu_package_name: Some("bluez"),
    tests: &[
        InstallTest::WhichSucceeds("bluetoothd"),
        InstallTest::FileExists("/usr/lib/systemd/system/bluetooth.service"),
    ],
};

pub const BLUEZ_UTILS_PACKAGE: RequiredPackage = RequiredPackage {
    name: "BlueZ utilities",
    arch_package_name: Some("bluez-utils"),
    ubuntu_package_name: Some("bluez"),
    tests: &[InstallTest::WhichSucceeds("bluetoothctl")],
};

pub const BLUEMAN_PACKAGE: RequiredPackage = RequiredPackage {
    name: "Blueman applet",
    arch_package_name: Some("blueman"),
    ubuntu_package_name: Some("blueman"),
    tests: &[InstallTest::WhichSucceeds("blueman-applet")],
};

pub const BLUETOOTH_CORE_PACKAGES: [RequiredPackage; 2] = [BLUEZ_PACKAGE, BLUEZ_UTILS_PACKAGE];

/// Check if the bluetooth service is currently active
fn bluetooth_service_active() -> bool {
    crate::common::systemd::SystemdManager::system().is_active("bluetooth")
}

// Requirement definitions for common use cases
pub const WIREMIX_REQUIREMENT: SettingRequirement = SettingRequirement::Package(WIREMIX_PACKAGE);

pub const BLUETOOTH_SERVICE_REQUIREMENT: SettingRequirement = SettingRequirement::Condition {
    description: "Bluetooth service must be running",
    check: bluetooth_service_active,
    resolve_hint: "Enable the Bluetooth service in Settings > Connectivity > Bluetooth",
};

pub const BLUETOOTH_MANAGER_REQUIREMENTS: [SettingRequirement; 4] = [
    SettingRequirement::Package(BLUEZ_PACKAGE),
    SettingRequirement::Package(BLUEZ_UTILS_PACKAGE),
    SettingRequirement::Package(BLUEMAN_PACKAGE),
    BLUETOOTH_SERVICE_REQUIREMENT,
];

pub const CATEGORIES: &[SettingCategory] = &[
    SettingCategory {
        id: "appearance",
        title: "Appearance",
        description: "Theme and visual presentation of the desktop.",
        icon: NerdFont::Lightbulb,
    },
    SettingCategory {
        id: "desktop",
        title: "Desktop",
        description: "Interactive desktop behaviour and helpers.",
        icon: NerdFont::Desktop,
    },
    SettingCategory {
        id: "workspace",
        title: "Workspace",
        description: "Window manager defaults and layout preferences.",
        icon: NerdFont::Folder,
    },
    SettingCategory {
        id: "audio",
        title: "Audio",
        description: "Sound routing tools and audio behaviour.",
        icon: NerdFont::VolumeUp,
    },
    SettingCategory {
        id: "system",
        title: "System",
        description: "System administration and user management.",
        icon: NerdFont::Server,
    },
    SettingCategory {
        id: "apps",
        title: "Applications",
        description: "Default applications and file associations.",
        icon: NerdFont::Package,
    },
    SettingCategory {
        id: "connectivity",
        title: "Connectivity",
        description: "Bluetooth and wireless device access.",
        icon: NerdFont::Bluetooth,
    },
    SettingCategory {
        id: "storage",
        title: "Storage",
        description: "Disk management and auto-mounting.",
        icon: NerdFont::Save,
    },
];

pub const SETTINGS: &[SettingDefinition] = &[
    SettingDefinition {
        id: "appearance.autotheming",
        title: "Autotheming",
        category: "appearance",
        icon: NerdFont::Palette,
        breadcrumbs: &["Autotheming"],
        kind: SettingKind::Toggle {
            key: BoolSettingKey::new("appearance.autotheming", true),
            summary: "Enable instantOS theming (disable for custom GTK themes).\n\nNote: Placeholder only; changing this setting currently has no effect.",
            apply: None,
        },
        requires_reapply: false,
        requirements: &[],
    },
    SettingDefinition {
        id: "appearance.animations",
        title: "Animations",
        category: "appearance",
        icon: NerdFont::Magic,
        breadcrumbs: &["Animations"],
        kind: SettingKind::Toggle {
            key: BoolSettingKey::new("appearance.animations", true),
            summary: "Controls desktop animation effects.\n\nNote: Placeholder only; changing this setting currently has no effect.",
            apply: None,
        },
        requires_reapply: false,
        requirements: &[],
    },
    SettingDefinition {
        id: "desktop.clipboard",
        title: "Clipboard manager",
        category: "desktop",
        icon: NerdFont::Clipboard,
        breadcrumbs: &["Clipboard manager"],
        kind: SettingKind::Toggle {
            key: BoolSettingKey::new("desktop.clipboard", true),
            summary: "Toggle the clipmenud clipboard manager.",
            apply: Some(super::actions::apply_clipboard_manager),
        },
        requires_reapply: false,
        requirements: &[],
    },
    SettingDefinition {
        id: "workspace.layout",
        title: "Default layout",
        category: "workspace",
        icon: NerdFont::List,
        breadcrumbs: &["Default layout"],
        kind: SettingKind::Choice {
            key: StringSettingKey::new("workspace.layout", "tile"),
            summary: "Select the default instantWM layout.",
            options: &[
                SettingOption {
                    value: "tile",
                    label: "Tile",
                    description: "Classic tiling layout",
                },
                SettingOption {
                    value: "grid",
                    label: "Grid",
                    description: "Balanced grid layout",
                },
                SettingOption {
                    value: "float",
                    label: "Float",
                    description: "Floating window placement",
                },
                SettingOption {
                    value: "monocle",
                    label: "Monocle",
                    description: "Single maximized window",
                },
                SettingOption {
                    value: "tcl",
                    label: "TCL",
                    description: "Three column layout",
                },
                SettingOption {
                    value: "deck",
                    label: "Deck",
                    description: "Primary window with stack",
                },
                SettingOption {
                    value: "overviewlayout",
                    label: "Overview",
                    description: "Overview of workspaces",
                },
                SettingOption {
                    value: "bstack",
                    label: "Bottom stack",
                    description: "Primary window with bottom stack",
                },
                SettingOption {
                    value: "bstackhoriz",
                    label: "Bottom stack horizontal",
                    description: "Bottom stack with horizontal split",
                },
            ],
            apply: None,
        },
        requires_reapply: false,
        requirements: &[],
    },
    SettingDefinition {
        id: "audio.wiremix",
        title: "General audio settings",
        category: "audio",
        icon: NerdFont::Settings,
        breadcrumbs: &["General audio settings"],
        kind: SettingKind::Command {
            summary: "Launch wiremix TUI to manage PipeWire routing and volumes.",
            command: CommandSpec::terminal("wiremix", &[]),
        },
        requires_reapply: false,
        requirements: &[WIREMIX_REQUIREMENT],
    },
    SettingDefinition {
        id: "system.user_management",
        title: "User management",
        category: "system",
        icon: NerdFont::Users2,
        breadcrumbs: &["User management"],
        kind: SettingKind::Action {
            summary: "Create and update Linux users, groups, and shells.",
            run: users::manage_users,
        },
        requires_reapply: false,
        requirements: &[],
    },
    SettingDefinition {
        id: "system.install_packages",
        title: "Install packages",
        category: "system",
        icon: NerdFont::Download,
        breadcrumbs: &["Install packages"],
        kind: SettingKind::Action {
            summary: "Browse and install system packages using an interactive fuzzy finder.",
            run: super::packages::run_package_installer_action,
        },
        requires_reapply: false,
        requirements: &[],
    },
    SettingDefinition {
        id: "connectivity.bluetooth.service",
        title: "Bluetooth service",
        category: "connectivity",
        icon: NerdFont::Bluetooth,
        breadcrumbs: &["Bluetooth", "Service"],
        kind: SettingKind::Toggle {
            key: BLUETOOTH_SERVICE_KEY,
            summary: "Enable the system Bluetooth service and keep it running.",
            apply: Some(super::actions::apply_bluetooth_service),
        },
        requires_reapply: false,
        requirements: &[],
    },
    SettingDefinition {
        id: "connectivity.bluetooth.manager",
        title: "Manage devices",
        category: "connectivity",
        icon: NerdFont::Bluetooth,
        breadcrumbs: &["Bluetooth", "Manage devices"],
        kind: SettingKind::Command {
            summary: "Launch Blueman manager to pair and manage Bluetooth devices.",
            command: CommandSpec::detached("blueman-manager", &[]),
        },
        requires_reapply: false,
        requirements: &BLUETOOTH_MANAGER_REQUIREMENTS,
    },
    SettingDefinition {
        id: "storage.automount",
        title: "Auto-mount disks",
        category: "storage",
        icon: NerdFont::HardDrive,
        breadcrumbs: &["Auto-mount disks"],
        kind: SettingKind::Toggle {
            key: UDISKIE_AUTOMOUNT_KEY,
            summary: "Automatically mount removable drives with udiskie.",
            apply: Some(super::actions::apply_udiskie_automount),
        },
        requires_reapply: false,
        requirements: &[],
    },
    SettingDefinition {
        id: "storage.disks",
        title: "Disk management",
        category: "storage",
        icon: NerdFont::HardDrive,
        breadcrumbs: &["Disk management"],
        kind: SettingKind::Command {
            summary: "Launch GNOME Disks to manage drives and partitions.",
            command: CommandSpec::detached("gnome-disks", &[]),
        },
        requires_reapply: false,
        requirements: &[SettingRequirement::Package(GNOME_DISKS_PACKAGE)],
    },
    SettingDefinition {
        id: "storage.gparted",
        title: "Partition editor",
        category: "storage",
        icon: NerdFont::Partition,
        breadcrumbs: &["Partition editor"],
        kind: SettingKind::Command {
            summary: "Launch GParted for advanced partition management (requires root).",
            command: CommandSpec::detached("gparted", &[]),
        },
        requires_reapply: false,
        requirements: &[SettingRequirement::Package(GPARTED_PACKAGE)],
    },
    SettingDefinition {
        id: "system.about",
        title: "About",
        category: "system",
        icon: NerdFont::About,
        breadcrumbs: &["About"],
        kind: SettingKind::Command {
            summary: "Display system information using fastfetch.",
            command: CommandSpec::terminal("sh", &["-c", "fastfetch && read -n 1"]),
        },
        requires_reapply: false,
        requirements: &[SettingRequirement::Package(FASTFETCH_PACKAGE)],
    },
    SettingDefinition {
        id: "apps.default",
        title: "Default applications",
        category: "apps",
        icon: NerdFont::Link,
        breadcrumbs: &["Default applications"],
        kind: SettingKind::Action {
            summary: "Manage default applications for file types and MIME types.",
            run: super::defaultapps::manage_default_apps,
        },
        requires_reapply: false,
        requirements: &[],
    },
    SettingDefinition {
        id: "system.upgrade",
        title: "Upgrade",
        category: "system",
        icon: NerdFont::Upgrade,
        breadcrumbs: &["Upgrade"],
        kind: SettingKind::Command {
            summary: "Upgrade all installed packages and system components using topgrade.",
            command: CommandSpec::terminal("topgrade", &[]),
        },
        requires_reapply: false,
        requirements: &[SettingRequirement::Package(TOPGRADE_PACKAGE)],
    },
];

pub fn category_by_id(id: &str) -> Option<&'static SettingCategory> {
    CATEGORIES.iter().find(|category| category.id == id)
}

pub fn settings_for_category(id: &str) -> Vec<&'static SettingDefinition> {
    SETTINGS
        .iter()
        .filter(|setting| setting.category == id)
        .collect()
}

pub fn setting_by_id(id: &str) -> Option<&'static SettingDefinition> {
    SETTINGS.iter().find(|setting| setting.id == id)
}
