mod system;
mod storage;
mod config;
mod notif_channels;

use std::sync::{ Arc, mpsc };
use std::time::Duration;
use ltk::{
	App, Element, Color, Keysym, Length, ThemeMode, ThemePreference,
	column, row, spacer, separator, slider,
	toggle, scroll, list_item, button, ButtonVariant, progress_bar, container, text_edit,
};

fn icon( name: &str ) -> Option<( Arc<Vec<u8>>, u32, u32 )>
{
	let ( rgba, w, h ) = ltk::theme_icon_rgba( name, 24 )?;
	let tinted = ltk::tint_symbolic( &rgba, ltk::theme_palette().icon );
	Some( ( Arc::new( tinted ), w, h ) )
}

macro_rules! t {
	( $s:expr ) => {
		ltk::text( $s ).color( ltk::theme_palette().text_primary )
	};
}

macro_rules! t_sec {
	( $s:expr ) => {
		ltk::text( $s ).color( ltk::theme_palette().text_secondary )
	};
}

#[derive( Clone )]
enum Msg
{
	Open( Screen ),
	Back,

	ToggleWifi,
	ToggleBluetooth,

	SetBrightness( f32 ),
	SetVolume( f32 ),
	ToggleMute,

	SetPowerProfile( String ),

	ToggleNightLight,
	SetNightLightTemp( u32 ),
	SetUiScale( f32 ),
	SetSleepTimeout( u32 ),
	LockRotation( system::Rotation ),
	UnlockRotation,

	ToggleDnd,
	ToggleLockscreenNotifs,
	ToggleChannelBlocked( String, String ),
	ToggleLocation,
	ToggleAirplane,
	ToggleHotspot,
	LaunchApp( String ),

	SetThemePref( ThemePreference ),
	SetWallpaperVariant( ThemeMode ),

	SearchInput( String ),
	Refresh,
	TriggerUpdateCheck,
}

#[derive( Clone, PartialEq )]
enum Screen
{
	Home,
	About,
	SystemAppUpdater,
	SecurityStatus,
	Wifi,
	Bluetooth,
	Cellular,
	Interconnectivity,
	PortableHotspot,
	MoreConnectivity,
	LockScreen,
	Notifications,
	HomeScreenSettings,
	Display,
	Sound,
	Wallpaper,
	Themes,
	FingerprintAndLock,
	SecurityEmergencies,
	PrivacyProtection,
	Apps,
	Battery,
	DigitalWellbeing,
	Account,
	Privacy,
	Location,
	Storage,
	Language,
	DateTime,
	Accessibility,
}

// On-demand scan results (wifi network list, bt devices, notification channels)
// delivered from one-shot threads spawned when the user opens those screens.
enum ScanResult
{
	Wifi(  Vec<system::WifiNetwork> ),
	Bt(    Vec<system::BluetoothDevice> ),
	Notif( Vec<notif_channels::ChannelInfo> ),
}

// Fields that require spawning external processes — collected in a background
// thread so the UI thread is never blocked waiting for nmcli / bluetoothctl /
// gsettings / wpctl / upower to respond.
struct SlowSnapshot
{
	wifi_on:                bool,
	connected_ssid:         Option<String>,
	bt_on:                  bool,
	volume:                 f32,
	muted:                  bool,
	battery_time_secs:      Option<i64>,
	power_profile:          String,
	night_light_on:         bool,
	night_light_temp:       u32,
	ui_scale:               f32,
	sleep_timeout:          u32,
	primary_ip:             Option<String>,
	dnd_enabled:            bool,
	lockscreen_show_notifs: bool,
	location_enabled:       bool,
	airplane_mode:          bool,
	hotspot_active:         bool,
	firewall_active:        bool,
	selinux_state:          Option<String>,
	sim_imei:               Option<String>,
	sim_operator:           Option<String>,
	fprintd_active:         bool,
	fprintd_enrolled:       bool,
	vpn_connections:        Vec<String>,
}

fn spawn_slow( tx: mpsc::Sender<SlowSnapshot> )
{
	std::thread::spawn( move ||
	{
		// Single wpctl call yields both volume and mute (see `audio_state`).
		let ( volume, muted ) = system::audio_state();
		let ( sim_imei, sim_operator ) = system::sim_info();
		let _ = tx.send( SlowSnapshot
		{
			wifi_on:                system::wifi_enabled(),
			connected_ssid:         system::connected_wifi_ssid(),
			bt_on:                  system::bluetooth_enabled(),
			volume,
			muted,
			battery_time_secs:      system::battery_time_remaining_secs(),
			power_profile:          system::power_profile(),
			night_light_on:         system::night_light_enabled(),
			night_light_temp:       system::night_light_temp(),
			ui_scale:               system::ui_scale(),
			sleep_timeout:          system::sleep_timeout_secs(),
			primary_ip:             system::primary_ip(),
			dnd_enabled:            system::dnd_enabled(),
			lockscreen_show_notifs: system::lockscreen_show_notifications(),
			location_enabled:       system::location_enabled(),
			airplane_mode:          system::airplane_mode(),
			hotspot_active:         system::hotspot_active(),
			firewall_active:        system::firewall_active(),
			selinux_state:          system::selinux_state(),
			sim_imei,
			sim_operator,
			fprintd_active:         system::fprintd_active(),
			fprintd_enrolled:       system::fprintd_enrolled(),
			vpn_connections:        system::vpn_connections(),
		});
	});
}

/// One-shot, separate from the recurring poll: `dnf/apt check-update` is the
/// single most expensive call in the whole app (metadata refresh, disk/network
/// I/O, multiple seconds) and the result almost never changes during a session.
/// Run it once on startup in its own thread instead of every 5 s slow refresh.
fn spawn_updates_check( tx: mpsc::Sender<Option<u32>> )
{
	std::thread::spawn( move ||
	{
		let _ = tx.send( system::updates_available() );
	});
}

struct State
{
	screen:        Screen,

	wifi_on:        bool,
	connected_ssid: Option<String>,
	wifi_networks:  Vec<system::WifiNetwork>,

	bt_on:         bool,
	bt_devices:    Vec<system::BluetoothDevice>,

	brightness:    f32,

	volume:        f32,
	muted:         bool,

	battery_pct:         Option<u8>,
	battery_chg:         bool,
	battery_present:     bool,
	battery_full_design: Option<u64>,
	battery_full:        Option<u64>,
	battery_cycles:      Option<u32>,
	battery_time_secs:   Option<i64>,
	ac_online:           bool,
	power_profile:       String,

	night_light_on:  bool,
	night_light_temp: u32,
	ui_scale:        f32,
	sleep_timeout:   u32,

	hostname:    String,
	kernel:      String,
	os_version:  String,
	cpu_model:   String,
	cpu_cores:   usize,
	total_memory: u64,
	used_memory:  u64,
	uptime_secs:  u64,
	primary_ip:   Option<String>,
	username:     String,

	dnd_enabled:           bool,
	lockscreen_show_notifs: bool,
	location_enabled:      bool,
	airplane_mode:         bool,
	hotspot_active:        bool,

	installed_apps:    Vec<system::AppEntry>,
	updates_available: Option<u32>,
	firewall_active:   bool,
	selinux_state:     Option<String>,
	sim_imei:          Option<String>,
	sim_operator:      Option<String>,
	fprintd_active:    bool,
	fprintd_enrolled:  bool,
	vpn_connections:   Vec<String>,
	nfc_available:     bool,
	usb_mode:          String,

	storage:      Vec<storage::MountInfo>,
	search_query: String,

	notif_channels: Vec<notif_channels::ChannelInfo>,

	theme_pref:        ThemePreference,
	wallpaper_variant: ThemeMode,

	slow_tx: mpsc::Sender<SlowSnapshot>,
	slow_rx: mpsc::Receiver<SlowSnapshot>,
	// True while a background slow-refresh thread is running. Prevents
	// `poll_external` (called on every event-loop iteration, not just on the
	// timer) from spawning a fresh pile of subprocesses on every wake-up.
	slow_in_flight:  bool,
	last_slow_spawn: std::time::Instant,
	// One-shot updates check delivered separately from the recurring refresh.
	updates_rx: mpsc::Receiver<Option<u32>>,
	// On-demand scan channel — wifi list, bt list, notif channels.
	scan_tx:       mpsc::SyncSender<ScanResult>,
	scan_rx:       mpsc::Receiver<ScanResult>,
	wifi_scanning: bool,
	bt_scanning:   bool,
	notif_scanning: bool,
}

impl State
{
	fn new() -> Self
	{
		let ( tx, rx ) = mpsc::channel();
		spawn_slow( tx.clone() );

		let ( utx, urx ) = mpsc::channel();
		spawn_updates_check( utx );

		let ( stx, srx ) = mpsc::sync_channel( 4 );

		let ( cpu_model, cpu_cores )     = system::cpu_info();
		let ( total_memory, used_memory ) = system::memory_info();

		Self
		{
			screen:         Screen::Home,
			// Fast sysfs reads — safe to call synchronously
			wifi_on:        false,
			connected_ssid: None,
			wifi_networks:  Vec::new(),
			bt_on:          false,
			bt_devices:     Vec::new(),
			brightness:    system::brightness(),
			volume:        0.5,
			muted:         false,
			battery_pct:         system::battery_percent(),
			battery_chg:         system::battery_charging(),
			battery_present:     system::battery_present(),
			battery_full_design: system::battery_full_design(),
			battery_full:        system::battery_full(),
			battery_cycles:      system::battery_cycle_count(),
			battery_time_secs:   None,
			ac_online:           system::ac_online(),
			power_profile:       "balanced".into(),
			night_light_on:      false,
			night_light_temp:    4700,
			ui_scale:            1.0,
			sleep_timeout:       0,
			// Static info — sysfs / procfs / env, always fast
			hostname:     system::hostname(),
			kernel:       system::kernel_version(),
			os_version:   system::os_version(),
			cpu_model,
			cpu_cores,
			total_memory,
			used_memory,
			uptime_secs:  system::uptime_secs(),
			primary_ip:   None,
			username:     system::username(),
			// Slow defaults — real values arrive from the background thread
			dnd_enabled:            false,
			lockscreen_show_notifs: true,
			location_enabled:       false,
			airplane_mode:          false,
			hotspot_active:         false,
			installed_apps:    Vec::new(),
			updates_available: None,
			firewall_active:   false,
			selinux_state:     None,
			sim_imei:          None,
			sim_operator:      None,
			fprintd_active:    false,
			fprintd_enrolled:  false,
			vpn_connections:   Vec::new(),
			nfc_available:     system::nfc_available(),
			usb_mode:          system::usb_mode(),
			storage:      Vec::new(),
			search_query: String::new(),
			notif_channels: Vec::new(),
			theme_pref:        config::read_theme_pref(),
			wallpaper_variant: config::read_wallpaper_variant(),
			slow_tx: tx,
			slow_rx: rx,
			slow_in_flight:  true, // spawn_slow above is already running
			last_slow_spawn: std::time::Instant::now(),
			updates_rx: urx,
			scan_tx:       stx,
			scan_rx:       srx,
			wifi_scanning: false,
			bt_scanning:   false,
			notif_scanning: false,
		}
	}

	// Only sysfs / procfs reads — never blocks.
	fn refresh_fast( &mut self )
	{
		self.brightness      = system::brightness();
		self.battery_pct     = system::battery_percent();
		self.battery_chg     = system::battery_charging();
		self.battery_present = system::battery_present();
		self.battery_full    = system::battery_full();
		self.battery_cycles  = system::battery_cycle_count();
		self.ac_online       = system::ac_online();
		self.used_memory     = system::used_memory();
		self.uptime_secs     = system::uptime_secs();
	}
}

impl App for State
{
	type Message = Msg;

	fn view( &self ) -> Element<Msg>
	{
		match self.screen
		{
			Screen::Home               => view_home( self ),
			Screen::About              => view_about( self ),
			Screen::SystemAppUpdater   => view_system_updater( self ),
			Screen::SecurityStatus     => view_security_status( self ),
			Screen::Wifi               => view_wifi( self ),
			Screen::Bluetooth          => view_bluetooth( self ),
			Screen::Cellular           => view_cellular( self ),
			Screen::Interconnectivity  => view_interconnectivity( self ),
			Screen::PortableHotspot    => view_portable_hotspot( self ),
			Screen::MoreConnectivity   => view_more_connectivity( self ),
			Screen::LockScreen         => view_lock_screen( self ),
			Screen::Notifications      => view_notifications( self ),
			Screen::HomeScreenSettings => view_home_screen( self ),
			Screen::Display            => view_display( self ),
			Screen::Sound              => view_sound( self ),
			Screen::Wallpaper          => view_wallpaper( self ),
			Screen::Themes             => view_themes( self ),
			Screen::FingerprintAndLock => view_fingerprint( self ),
			Screen::SecurityEmergencies => view_security_emergencies( self ),
			Screen::PrivacyProtection  => view_privacy_protection( self ),
			Screen::Apps               => view_apps( self ),
			Screen::Battery            => view_battery( self ),
			Screen::DigitalWellbeing   => view_digital_wellbeing( self ),
			Screen::Account            => view_account( self ),
			Screen::Privacy            => view_privacy( self ),
			Screen::Location           => view_location( self ),
			Screen::Storage            => view_storage( self ),
			Screen::Language           => view_language( self ),
			Screen::DateTime           => view_datetime( self ),
			Screen::Accessibility      => view_accessibility( self ),
		}
	}

	fn update( &mut self, msg: Msg )
	{
		match msg
		{
			Msg::Open( s ) =>
			{
				match &s
				{
					Screen::Wifi =>
					{
						if !self.wifi_scanning
						{
							self.wifi_scanning = true;
							let tx = self.scan_tx.clone();
							std::thread::spawn( move || { let _ = tx.send( ScanResult::Wifi( system::wifi_networks() ) ); } );
						}
					}
					Screen::Bluetooth =>
					{
						if !self.bt_scanning
						{
							self.bt_scanning = true;
							let tx = self.scan_tx.clone();
							std::thread::spawn( move || { let _ = tx.send( ScanResult::Bt( system::bluetooth_devices() ) ); } );
						}
					}
					Screen::Notifications =>
					{
						if !self.notif_scanning
						{
							self.notif_scanning = true;
							let tx = self.scan_tx.clone();
							std::thread::spawn( move || { let _ = tx.send( ScanResult::Notif( notif_channels::load() ) ); } );
						}
					}
					Screen::Storage   => self.storage       = storage::scan(),
					Screen::Apps      =>
					{
						if self.installed_apps.is_empty()
						{
							self.installed_apps = system::installed_apps();
						}
					}
					_ => {}
				}
				self.screen = s;
			}
			Msg::Back => self.screen = Screen::Home,

			Msg::ToggleWifi =>
			{
				self.wifi_on = !self.wifi_on;
				system::set_wifi_enabled( self.wifi_on );
			}
			Msg::ToggleBluetooth =>
			{
				self.bt_on = !self.bt_on;
				system::set_bluetooth_enabled( self.bt_on );
			}
			Msg::SetBrightness( v ) =>
			{
				self.brightness = v;
				system::set_brightness( v );
			}
			Msg::SetVolume( v ) =>
			{
				self.volume = v;
				system::set_volume( v );
			}
			Msg::ToggleMute =>
			{
				self.muted = !self.muted;
				system::set_muted( self.muted );
			}
			Msg::SetPowerProfile( p ) =>
			{
				system::set_power_profile( &p );
				self.power_profile = p;
			}
			Msg::ToggleNightLight =>
			{
				self.night_light_on = !self.night_light_on;
				system::set_night_light_enabled( self.night_light_on );
			}
			Msg::SetNightLightTemp( t ) =>
			{
				self.night_light_temp = t;
				system::set_night_light_temp( t );
			}
			Msg::SetUiScale( s ) =>
			{
				self.ui_scale = s;
				system::set_ui_scale( s );
			}
			Msg::SetSleepTimeout( t ) =>
			{
				self.sleep_timeout = t;
				system::set_sleep_timeout_secs( t );
			}
			Msg::LockRotation( r )  => system::lock_rotation( r ),
			Msg::UnlockRotation     => system::unlock_rotation(),

			Msg::ToggleDnd =>
			{
				self.dnd_enabled = !self.dnd_enabled;
				system::set_dnd_enabled( self.dnd_enabled );
			}
			Msg::ToggleLockscreenNotifs =>
			{
				self.lockscreen_show_notifs = !self.lockscreen_show_notifs;
				system::set_lockscreen_show_notifications( self.lockscreen_show_notifs );
			}
			Msg::ToggleLocation =>
			{
				self.location_enabled = !self.location_enabled;
				system::set_location_enabled( self.location_enabled );
			}
			Msg::ToggleAirplane =>
			{
				self.airplane_mode = !self.airplane_mode;
				system::set_airplane_mode( self.airplane_mode );
			}
			Msg::ToggleHotspot =>
			{
				self.hotspot_active = !self.hotspot_active;
				system::set_hotspot_enabled( self.hotspot_active );
			}
			Msg::LaunchApp( exec ) => system::launch_app( &exec ),

			Msg::SetThemePref( pref ) =>
			{
				use chrono::Timelike as _;
				self.theme_pref = pref;
				let hour = chrono::Local::now().hour();
				ltk::set_active_mode( pref.resolve( hour ) );
				config::write_theme_pref( pref );
			}
			Msg::SetWallpaperVariant( mode ) =>
			{
				self.wallpaper_variant = mode;
				config::write_wallpaper_variant( mode );
			}

			Msg::ToggleChannelBlocked( app_id, channel_id ) =>
			{
				if let Some( c ) = self.notif_channels.iter_mut().find( | c | c.app_id == app_id && c.channel_id == channel_id )
				{
					c.blocked = !c.blocked;
					notif_channels::set_blocked( &app_id, &channel_id, c.blocked );
				}
			}

			Msg::SearchInput( s )      => self.search_query = s,
			Msg::Refresh               => self.refresh_fast(),
			Msg::TriggerUpdateCheck    =>
			{
				let ( utx, urx ) = mpsc::channel();
				self.updates_rx = urx;
				spawn_updates_check( utx );
			}
		}
	}

	fn on_key( &mut self, keysym: Keysym ) -> Option<Msg>
	{
		match keysym
		{
			Keysym::Escape =>
			{
				if self.screen != Screen::Home { return Some( Msg::Back ); }
				std::process::exit( 0 );
			}
			_ => None,
		}
	}

	fn poll_interval( &self ) -> Option<Duration>
	{
		Some( Duration::from_secs( 5 ) )
	}

	fn poll_external( &mut self ) -> Vec<Msg>
	{
		// NOTE: the ltk runtime calls this on *every* event-loop iteration
		// (frame callbacks, input, timer…), not only when `poll_interval`
		// fires. So this must be cheap and idempotent: never unconditionally
		// spawn work or return a redraw message, or we feed a redraw → wake →
		// poll → spawn loop that pegs the CPU and forks a subprocess storm.
		let mut changed = false;

		// Drain results delivered by the background slow-refresh thread.
		while let Ok( snap ) = self.slow_rx.try_recv()
		{
			self.wifi_on                = snap.wifi_on;
			self.connected_ssid         = snap.connected_ssid;
			self.bt_on                  = snap.bt_on;
			self.volume                 = snap.volume;
			self.muted                  = snap.muted;
			self.battery_time_secs      = snap.battery_time_secs;
			self.power_profile          = snap.power_profile;
			self.night_light_on         = snap.night_light_on;
			self.night_light_temp       = snap.night_light_temp;
			self.ui_scale               = snap.ui_scale;
			self.sleep_timeout          = snap.sleep_timeout;
			self.primary_ip             = snap.primary_ip;
			self.dnd_enabled            = snap.dnd_enabled;
			self.lockscreen_show_notifs = snap.lockscreen_show_notifs;
			self.location_enabled       = snap.location_enabled;
			self.airplane_mode          = snap.airplane_mode;
			self.hotspot_active         = snap.hotspot_active;
			self.firewall_active        = snap.firewall_active;
			self.selinux_state          = snap.selinux_state;
			self.sim_imei               = snap.sim_imei;
			self.sim_operator           = snap.sim_operator;
			self.fprintd_active         = snap.fprintd_active;
			self.fprintd_enrolled       = snap.fprintd_enrolled;
			self.vpn_connections        = snap.vpn_connections;
			self.slow_in_flight         = false;
			changed = true;
		}

		// Drain the one-shot updates check.
		while let Ok( n ) = self.updates_rx.try_recv()
		{
			self.updates_available = n;
			changed = true;
		}

		// Drain on-demand scan results.
		while let Ok( result ) = self.scan_rx.try_recv()
		{
			match result
			{
				ScanResult::Wifi( nets )  => { self.wifi_networks = nets;  self.wifi_scanning  = false; }
				ScanResult::Bt( devs )    => { self.bt_devices    = devs;  self.bt_scanning    = false; }
				ScanResult::Notif( chs )  => { self.notif_channels = chs; self.notif_scanning = false; }
			}
			changed = true;
		}

		// Kick off the next slow refresh only when the previous one has
		// finished *and* at least the poll interval has elapsed — one
		// background pass at a time, no faster than every 5 s.
		if !self.slow_in_flight
			&& self.last_slow_spawn.elapsed() >= Duration::from_secs( 5 )
		{
			self.last_slow_spawn = std::time::Instant::now();
			self.slow_in_flight  = true;
			spawn_slow( self.slow_tx.clone() );
		}

		// Only ask for a redraw when fresh data actually arrived. Returning
		// an empty vec lets the event loop go back to sleep.
		if changed { vec![ Msg::Refresh ] } else { vec![] }
	}

	fn window_config( &self ) -> Option<(&str, &str)>
	{
		Some( ( "Eydos Settings", "eydos-settings" ) )
	}
}

// ── Shared helpers ───────────────────────────────────────────────────────────

fn header( title: &str ) -> Element<Msg>
{
	column::<Msg>()
		.spacing( 4.0 )
		.push(
			button( "← Back" )
				.variant( ButtonVariant::Tertiary )
				.on_press( Msg::Back )
		)
		.push( t!( title ).size( 24.0 ) )
		.push( separator() )
		.into()
}

fn section_sep() -> Element<Msg> { separator().into() }

macro_rules! row_link {
	( $label:expr, $icon_path:expr, $screen:expr ) => {{
		let mut it = ltk::list_item( $label )
			.trailing( "›" )
			.on_press( Msg::Open( $screen ) );
		if let Some( ( rgba, w, h ) ) = icon( $icon_path )
		{
			it = it.icon( rgba, w, h );
		}
		it
	}};
}

fn fmt_uptime( secs: u64 ) -> String
{
	let days  = secs / 86400;
	let hours = ( secs % 86400 ) / 3600;
	let mins  = ( secs % 3600 ) / 60;
	if days > 0      { format!( "{}d {}h {}m", days, hours, mins ) }
	else if hours > 0 { format!( "{}h {}m", hours, mins ) }
	else             { format!( "{}m", mins ) }
}

// ── Home ─────────────────────────────────────────────────────────────────────

fn home_wifi_row( state: &State ) -> Element<Msg>
{
	let mut it = ltk::list_item( "Wi-Fi" )
		.trailing( "›" )
		.on_press( Msg::Open( Screen::Wifi ) );
	if state.wifi_on
	{
		if let Some( ssid ) = state.connected_ssid.as_deref()
		{
			it = it.subtitle( ssid );
		}
		else
		{
			it = it.subtitle( "On" );
		}
	}
	if let Some( ( rgba, w, h ) ) = icon( "system/wifi-signal-full" ) { it = it.icon( rgba, w, h ); }
	it.into()
}

fn home_battery_row( state: &State ) -> Element<Msg>
{
	let trailing = state.battery_pct
		.map( |p| format!( "{}%", p ) )
		.unwrap_or_else( || "›".into() );
	let mut it = ltk::list_item( "Battery" )
		.trailing( &trailing )
		.on_press( Msg::Open( Screen::Battery ) );
	if let Some( ( rgba, w, h ) ) = icon( "energy/battery-full" ) { it = it.icon( rgba, w, h ); }
	it.into()
}

fn view_home( state: &State ) -> Element<Msg>
{
	let q = state.search_query.trim().to_lowercase();

	// Flat entry table used both for search and grouped layout
	let entries: &[( &str, &str, Screen )] = &[
		( "About this phone",                              "feedback/information",                    Screen::About              ),
		( "System App Updater",                            "hardware/chip",                           Screen::SystemAppUpdater   ),
		( "Security Status",                               "safety/administrator-shield",             Screen::SecurityStatus     ),
		( "Wi-Fi",                                         "system/wifi-signal-full",                 Screen::Wifi               ),
		( "Bluetooth",                                     "system/bluetooth-connected",              Screen::Bluetooth          ),
		( "Mobile Networks",                               "system/sim-card",                         Screen::Cellular           ),
		( "Interconnectivity",                             "system/ethernet",                         Screen::Interconnectivity  ),
		( "Portable Hotspot",                              "system/hotspot-access-point",             Screen::PortableHotspot    ),
		( "More Connectivity Options",                     "general/right-circle",                    Screen::MoreConnectivity   ),
		( "Lock Screen",                                   "safety/lock-screen",                      Screen::LockScreen         ),
		( "Notifications and Status bar",                  "communication/notifications-status",      Screen::Notifications      ),
		( "Home Screen",                                   "navigation/home",                         Screen::HomeScreenSettings ),
		( "Display and Brightness",                        "hardware/brightness-maximum",             Screen::Display            ),
		( "Sound and Vibration",                           "hardware/speaker-high",                   Screen::Sound              ),
		( "Wallpaper",                                     "customisation/wallpaper",                 Screen::Wallpaper          ),
		( "Themes",                                        "customisation/colour-palette",            Screen::Themes             ),
		( "Fingerprint, Facial data, and Screen lock",     "safety/fingerprint",                      Screen::FingerprintAndLock ),
		( "Security and Emergencies",                      "safety/security-and-emergencies",         Screen::SecurityEmergencies),
		( "Privacy Protection",                            "safety/padlock",                          Screen::PrivacyProtection  ),
		( "Apps",                                          "features/app-store",                      Screen::Apps               ),
		( "Battery",                                       "energy/battery-full",                     Screen::Battery            ),
		( "Digital Wellbeing and Parental Control",        "feedback/hourglass",                      Screen::DigitalWellbeing   ),
		( "Account",                                       "communication/contact",                   Screen::Account            ),
		( "Privacy",                                       "safety/face-id",                          Screen::Privacy            ),
		( "Location",                                      "features/maps-gps",                       Screen::Location           ),
		( "Storage",                                       "hardware/drive",                          Screen::Storage            ),
		( "Region & Language",                             "general/text",                            Screen::Language           ),
		( "Date & Time",                                   "general/time",                            Screen::DateTime           ),
		( "Accessibility",                                 "features/accessibility",                  Screen::Accessibility      ),
	];

	let list_content: Element<Msg> = if !q.is_empty()
	{
		let mut results = column::<Msg>().spacing( 2.0 );
		let mut found   = false;
		for ( label, icon_path, screen ) in entries
		{
			if label.to_lowercase().contains( &q )
			{
				results = results.push( row_link!( *label, *icon_path, screen.clone() ) );
				found = true;
			}
		}
		if !found
		{
			results = results.push( t_sec!( "No results" ).size( 14.0 ) );
		}
		scroll( results ).into()
	}
	else
	{
		scroll(
			column::<Msg>()
				.spacing( 2.0 )
				// ── Group 1 ─────────────────────────────────────
				.push( row_link!( "About this phone",      "feedback/information",          Screen::About            ) )
				.push( row_link!( "System App Updater",    "hardware/chip",                 Screen::SystemAppUpdater ) )
				.push( row_link!( "Security Status",       "safety/administrator-shield",   Screen::SecurityStatus   ) )
				.push( separator() )
				// ── Group 2 ─────────────────────────────────────
				.push( home_wifi_row( state ) )
				.push( row_link!( "Bluetooth",             "system/bluetooth-connected",    Screen::Bluetooth        ) )
				.push( row_link!( "Mobile Networks",       "system/sim-card",               Screen::Cellular         ) )
				.push( row_link!( "Interconnectivity",     "system/ethernet",               Screen::Interconnectivity) )
				.push( row_link!( "Portable Hotspot",      "system/hotspot-access-point",   Screen::PortableHotspot  ) )
				.push( row_link!( "More Connectivity Options", "general/right-circle",      Screen::MoreConnectivity ) )
				.push( separator() )
				// ── Group 3 ─────────────────────────────────────
				.push( row_link!( "Lock Screen",                "safety/lock-screen",            Screen::LockScreen         ) )
				.push( row_link!( "Notifications and Status bar", "communication/notifications-status", Screen::Notifications ) )
				.push( row_link!( "Home Screen",                "navigation/home",               Screen::HomeScreenSettings ) )
				.push( row_link!( "Display and Brightness",     "hardware/brightness-maximum",   Screen::Display            ) )
				.push( row_link!( "Sound and Vibration",        "hardware/speaker-high",         Screen::Sound              ) )
				.push( row_link!( "Wallpaper",                  "customisation/wallpaper",       Screen::Wallpaper          ) )
				.push( row_link!( "Themes",                     "customisation/colour-palette",  Screen::Themes             ) )
				.push( separator() )
				// ── Group 4 ─────────────────────────────────────
				.push( row_link!( "Fingerprint, Facial data, and Screen lock", "safety/fingerprint", Screen::FingerprintAndLock ) )
				.push( row_link!( "Security and Emergencies",  "safety/security-and-emergencies", Screen::SecurityEmergencies ) )
				.push( row_link!( "Privacy Protection",        "safety/padlock",                  Screen::PrivacyProtection   ) )
				.push( row_link!( "Apps",                      "features/app-store",              Screen::Apps                ) )
				.push( home_battery_row( state ) )
				.push( separator() )
				// ── Group 5 ─────────────────────────────────────
				.push( row_link!( "Digital Wellbeing and Parental Control", "feedback/hourglass", Screen::DigitalWellbeing ) )
				.push( row_link!( "Account",                   "communication/contact",           Screen::Account             ) )
				.push( separator() )
				// ── Group 6 ─────────────────────────────────────
				.push( row_link!( "Privacy",                   "safety/face-id",                  Screen::Privacy             ) )
				.push( row_link!( "Location",                  "features/maps-gps",               Screen::Location            ) )
				.push( separator() )
				// ── Group 7 ─────────────────────────────────────
				.push( row_link!( "Storage",                   "hardware/drive",                  Screen::Storage             ) )
				.push( row_link!( "Region & Language",         "general/text",                    Screen::Language            ) )
				.push( row_link!( "Date & Time",               "general/time",                    Screen::DateTime            ) )
				.push( row_link!( "Accessibility",             "features/accessibility",          Screen::Accessibility       ) )
		).into()
	};

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 12.0 )
		.push( t!( "Settings" ).size( 28.0 ) )
		.push(
			text_edit( "Search in Settings", &state.search_query )
				.on_change( Msg::SearchInput )
		)
		.push( list_content )
		.into()
}

// ── About ────────────────────────────────────────────────────────────────────

fn view_about( state: &State ) -> Element<Msg>
{
	let mem_total = storage::fmt_bytes( state.total_memory );
	let mem_used  = storage::fmt_bytes( state.used_memory );
	let mem_str   = format!( "{} / {}", mem_used, mem_total );
	let cpu_str   = format!( "{} ({} cores)", state.cpu_model, state.cpu_cores );
	let uptime    = fmt_uptime( state.uptime_secs );
	let ip_str    = state.primary_ip.as_deref().unwrap_or( "Not connected" );

	scroll(
		column::<Msg>()
			.padding( 24.0 )
			.spacing( 4.0 )
			.push( header( "About this phone" ) )
			.push( list_item( "Device name" ).trailing( &state.hostname ) )
			.push( list_item( "User" ).trailing( &state.username ) )
			.push( section_sep() )
			.push( list_item( "Operating System" ).trailing( &state.os_version ) )
			.push( list_item( "Kernel" ).trailing( &state.kernel ) )
			.push( section_sep() )
			.push( list_item( "Processor" ).subtitle( &cpu_str ) )
			.push( list_item( "Memory" ).trailing( &mem_str ) )
			.push( section_sep() )
			.push( list_item( "Uptime" ).trailing( &uptime ) )
			.push( list_item( "IP address" ).trailing( ip_str ) )
			.push( spacer() )
	)
	.into()
}

// ── System App Updater ───────────────────────────────────────────────────────

fn view_system_updater( state: &State ) -> Element<Msg>
{
	let status = match state.updates_available
	{
		None     => "Not checked".to_string(),
		Some( 0 ) => "System is up to date".to_string(),
		Some( n ) => format!( "{} update{} available", n, if n == 1 { "" } else { "s" } ),
	};

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 12.0 )
		.push( header( "System App Updater" ) )
		.push( list_item( "Status" ).trailing( &status ) )
		.push( spacer() )
		.into()
}

// ── Security Status ──────────────────────────────────────────────────────────

fn view_security_status( state: &State ) -> Element<Msg>
{
	let fw_str = if state.firewall_active { "Active" } else { "Inactive" };
	let se_str = state.selinux_state.as_deref().unwrap_or( "Not available" );

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Security Status" ) )
		.push( list_item( "Firewall" ).trailing( fw_str ) )
		.push( list_item( "SELinux" ).trailing( se_str ) )
		.push( spacer() )
		.into()
}

// ── Wi-Fi ────────────────────────────────────────────────────────────────────

fn view_wifi( state: &State ) -> Element<Msg>
{
	let mut col = column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Wi-Fi" ) )
		.push( toggle( state.wifi_on ).label( "Wi-Fi" ).on_toggle( Msg::ToggleWifi ) );

	if state.wifi_on && !state.wifi_networks.is_empty()
	{
		col = col.push( separator() );
		col = col.push( t_sec!( "Networks" ).size( 13.0 ) );
		let mut net_col = column::<Msg>().spacing( 0.0 );
		for net in &state.wifi_networks
		{
			let signal = format!( "{}%", net.signal );
			let mut item = list_item( &net.ssid ).trailing( &signal );
			if net.connected { item = item.subtitle( "Connected" ); }
			net_col = net_col.push( item );
		}
		col = col.push( scroll( net_col ) );
	}
	else if state.wifi_on
	{
		col = col.push( separator() );
		col = col.push( t_sec!( "No networks found" ).size( 13.0 ) );
	}

	col.push( spacer() ).into()
}

// ── Bluetooth ────────────────────────────────────────────────────────────────

fn view_bluetooth( state: &State ) -> Element<Msg>
{
	let mut col = column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Bluetooth" ) )
		.push( toggle( state.bt_on ).label( "Bluetooth" ).on_toggle( Msg::ToggleBluetooth ) );

	if state.bt_on && !state.bt_devices.is_empty()
	{
		col = col.push( separator() );
		col = col.push( t_sec!( "Devices" ).size( 13.0 ) );
		let mut dev_col = column::<Msg>().spacing( 0.0 );
		for dev in &state.bt_devices
		{
			let mut item = list_item( &dev.name );
			if dev.connected { item = item.subtitle( "Connected" ); }
			dev_col = dev_col.push( item );
		}
		col = col.push( scroll( dev_col ) );
	}
	else if state.bt_on
	{
		col = col.push( separator() );
		col = col.push( t_sec!( "No paired devices" ).size( 13.0 ) );
	}

	col.push( spacer() ).into()
}

// ── Mobile Networks ──────────────────────────────────────────────────────────

fn view_cellular( state: &State ) -> Element<Msg>
{
	let no_sim = state.sim_imei.is_none() && state.sim_operator.is_none();

	let mut col = column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Mobile Networks" ) );

	if let Some( op ) = state.sim_operator.as_deref()
	{
		col = col.push( list_item( "Operator" ).trailing( op ) );
	}
	if let Some( imei ) = state.sim_imei.as_deref()
	{
		col = col.push( list_item( "IMEI" ).trailing( imei ) );
	}
	if no_sim
	{
		col = col.push( t_sec!( "No SIM card detected" ).size( 14.0 ) );
	}

	col.push( spacer() ).into()
}

// ── Interconnectivity ────────────────────────────────────────────────────────

fn view_interconnectivity( state: &State ) -> Element<Msg>
{
	let nfc_str = if state.nfc_available { "Available" } else { "Not available" };

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 8.0 )
		.push( header( "Interconnectivity" ) )
		.push( toggle( state.bt_on ).label( "Bluetooth" ).on_toggle( Msg::ToggleBluetooth ) )
		.push( separator() )
		.push( list_item( "NFC" ).trailing( nfc_str ) )
		.push( list_item( "USB" ).trailing( &state.usb_mode ) )
		.push( spacer() )
		.into()
}

// ── Portable Hotspot ─────────────────────────────────────────────────────────

fn view_portable_hotspot( state: &State ) -> Element<Msg>
{
	column::<Msg>()
		.padding( 24.0 )
		.spacing( 8.0 )
		.push( header( "Portable Hotspot" ) )
		.push(
			toggle( state.hotspot_active )
				.label( "Portable Hotspot" )
				.on_toggle( Msg::ToggleHotspot )
		)
		.push( separator() )
		.push( t_sec!( "Shares your mobile data over Wi-Fi." ).size( 13.0 ) )
		.push( spacer() )
		.into()
}

// ── More Connectivity ────────────────────────────────────────────────────────

fn view_more_connectivity( state: &State ) -> Element<Msg>
{
	let mut col = column::<Msg>()
		.padding( 24.0 )
		.spacing( 8.0 )
		.push( header( "More Connectivity Options" ) )
		.push( toggle( state.airplane_mode ).label( "Airplane Mode" ).on_toggle( Msg::ToggleAirplane ) )
		.push( separator() )
		.push( t_sec!( "VPN" ).size( 13.0 ) );

	if state.vpn_connections.is_empty()
	{
		col = col.push( list_item( "No VPN connections configured" ).trailing( "" ) );
	}
	else
	{
		for name in &state.vpn_connections
		{
			col = col.push( list_item( name ).trailing( "›" ) );
		}
	}

	col.push( spacer() ).into()
}

// ── Lock Screen ──────────────────────────────────────────────────────────────

fn view_lock_screen( state: &State ) -> Element<Msg>
{
	column::<Msg>()
		.padding( 24.0 )
		.spacing( 8.0 )
		.push( header( "Lock Screen" ) )
		.push(
			toggle( state.lockscreen_show_notifs )
				.label( "Show notifications" )
				.on_toggle( Msg::ToggleLockscreenNotifs )
		)
		.push( spacer() )
		.into()
}

// ── Notifications ────────────────────────────────────────────────────────────

fn view_notifications( state: &State ) -> Element<Msg>
{
	let mut col = column::<Msg>()
		.padding( 24.0 )
		.spacing( 8.0 )
		.push( header( "Notifications and Status bar" ) )
		.push(
			toggle( state.dnd_enabled )
				.label( "Do Not Disturb" )
				.on_toggle( Msg::ToggleDnd )
		)
		.push( separator() )
		.push(
			toggle( state.lockscreen_show_notifs )
				.label( "Show on lock screen" )
				.on_toggle( Msg::ToggleLockscreenNotifs )
		)
		.push( section_sep() )
		.push( t_sec!( "App notification channels" ).size( 13.0 ) );

	if state.notif_channels.is_empty()
	{
		col = col.push( t_sec!( "No channels registered yet." ).size( 12.0 ) );
	}

	// One toggle per channel (on = allowed, off = blocked), grouped by app.
	let mut last_app = String::new();
	for c in &state.notif_channels
	{
		if c.app_id != last_app
		{
			col = col.push( t_sec!( c.app_id.clone() ).size( 12.0 ) );
			last_app = c.app_id.clone();
		}
		col = col.push(
			toggle( !c.blocked )
				.label( c.name.clone() )
				.on_toggle( Msg::ToggleChannelBlocked( c.app_id.clone(), c.channel_id.clone() ) )
		);
	}

	scroll( col.push( spacer() ) ).into()
}

// ── Home Screen ──────────────────────────────────────────────────────────────

fn view_home_screen( _state: &State ) -> Element<Msg>
{
	column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Home Screen" ) )
		.push( separator() )
		.push( t_sec!( "Swipe up from the bottom to go home." ).size( 13.0 ) )
		.push( t_sec!( "Swipe down from the top to open the quick panel." ).size( 13.0 ) )
		.push( spacer() )
		.into()
}

// ── Display ──────────────────────────────────────────────────────────────────

fn view_display( state: &State ) -> Element<Msg>
{
	let bri_label  = format!( "Brightness: {:.0}%", state.brightness * 100.0 );
	let nl_label   = format!( "Night Light: {} K", state.night_light_temp );
	let nl_norm    = ( state.night_light_temp as f32 - 1000.0 ) / 5500.0;
	let scale_pct  = ( state.ui_scale * 100.0 ).round() as u32;
	let sleep_min  = if state.sleep_timeout == 0 { "Never".to_string() }
	                 else { format!( "{} min", state.sleep_timeout / 60 ) };
	let sleep_norm = state.sleep_timeout as f32 / 60.0 / 30.0;

	let mut col = column::<Msg>()
		.padding( 24.0 )
		.spacing( 12.0 )
		.push( header( "Display and Brightness" ) )
		// ── Brightness ──────────────────────────────────────────────────
		.push( t_sec!( bri_label ).size( 13.0 ) )
		.push( slider( state.brightness ).on_change( Msg::SetBrightness ) )
		// ── Night Light ─────────────────────────────────────────────────
		.push( section_sep() )
		.push( toggle( state.night_light_on ).label( "Night Light" ).on_toggle( Msg::ToggleNightLight ) );

	if state.night_light_on
	{
		col = col.push( t_sec!( nl_label ).size( 13.0 ) );
		col = col.push(
			slider( nl_norm.clamp( 0.0, 1.0 ) )
				.on_change( |v| Msg::SetNightLightTemp( ( 1000.0 + v * 5500.0 ) as u32 ) )
		);
	}

	// ── Rotation ────────────────────────────────────────────────────────
	col = col.push( section_sep() );
	col = col.push( t_sec!( "Rotation" ).size( 13.0 ) );
	col = col.push(
		row::<Msg>()
			.spacing( 6.0 )
			.push( rot_btn( "Portrait",    system::Rotation::Portrait        ) )
			.push( rot_btn( "Landscape",   system::Rotation::Landscape       ) )
			.push( rot_btn( "↓ Portrait",  system::Rotation::InversePortrait ) )
			.push( rot_btn( "↓ Landscape", system::Rotation::InverseLandscape ) )
	);
	col = col.push(
		button( "Auto rotate" )
			.variant( ButtonVariant::Tertiary )
			.on_press( Msg::UnlockRotation )
	);

	// ── Scale ────────────────────────────────────────────────────────────
	col = col.push( section_sep() );
	col = col.push( t_sec!( format!( "Display Scale: {}%", scale_pct ) ).size( 13.0 ) );
	col = col.push(
		row::<Msg>()
			.spacing( 6.0 )
			.push( scale_btn( "100%", 1.00, state.ui_scale ) )
			.push( scale_btn( "125%", 1.25, state.ui_scale ) )
			.push( scale_btn( "150%", 1.50, state.ui_scale ) )
			.push( scale_btn( "200%", 2.00, state.ui_scale ) )
	);

	// ── Sleep ─────────────────────────────────────────────────────────────
	col = col.push( section_sep() );
	col = col.push( t_sec!( format!( "Screen Sleep: {}", sleep_min ) ).size( 13.0 ) );
	col = col.push(
		slider( sleep_norm.clamp( 0.0, 1.0 ) )
			.on_change( |v| Msg::SetSleepTimeout( ( v * 30.0 ).round() as u32 * 60 ) )
	);

	col.push( spacer() ).into()
}

fn rot_btn( label: &str, r: system::Rotation ) -> Element<Msg>
{
	button( label )
		.variant( ButtonVariant::Secondary )
		.on_press( Msg::LockRotation( r ) )
		.into()
}

fn scale_btn( label: &str, value: f32, current: f32 ) -> Element<Msg>
{
	let active = ( value - current ).abs() < 0.01;
	button( label )
		.variant( if active { ButtonVariant::Primary } else { ButtonVariant::Secondary } )
		.on_press( Msg::SetUiScale( value ) )
		.into()
}

// ── Sound ────────────────────────────────────────────────────────────────────

fn view_sound( state: &State ) -> Element<Msg>
{
	let vol_label  = format!( "Volume: {:.0}%", state.volume * 100.0 );
	let mute_label = if state.muted { "Muted" } else { "Unmuted" };

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 8.0 )
		.push( header( "Sound and Vibration" ) )
		.push( t_sec!( vol_label ).size( 14.0 ) )
		.push( slider( state.volume ).on_change( Msg::SetVolume ) )
		.push( separator() )
		.push( toggle( state.muted ).label( mute_label ).on_toggle( Msg::ToggleMute ) )
		.push( spacer() )
		.into()
}

// ── Wallpaper ────────────────────────────────────────────────────────────────

fn view_wallpaper( state: &State ) -> Element<Msg>
{
	// Resolve the wallpaper through ltk so we display the actual file the
	// shell would load. Try a raster first (highest-quality default) and
	// fall back to the SVG variant; ltk handles the light/dark fallback
	// internally based on the *active* mode, so we don't need to do that
	// here — but we do show the user-chosen variant for clarity.
	let resolved = ltk::theme_branding_raster( "wallpaper", 0, 0 )
		.or_else( || ltk::theme_branding_asset( "wallpaper", "svg" ) )
		.or_else( || ltk::theme_branding_asset( "wallpaper", "webp" ) );

	let variant_lbl = match state.wallpaper_variant
	{
		ThemeMode::Light => "Light variant",
		ThemeMode::Dark  => "Dark variant",
	};

	let mut col = column::<Msg>()
		.padding( 24.0 )
		.spacing( 12.0 )
		.push( header( "Wallpaper" ) )
		.push( t_sec!( "Current wallpaper" ).size( 13.0 ) )
		.push( list_item( "Name" ).trailing( "Default" ) )
		.push( list_item( "Variant" ).trailing( variant_lbl ) );

	if let Some( path ) = resolved.as_ref()
	{
		col = col.push( list_item( "File" ).subtitle( &path.display().to_string() ) );
	}
	else
	{
		col = col.push( t_sec!( "Wallpaper file not found in active theme." ).size( 13.0 ) );
	}

	col = col.push( section_sep() );
	col = col.push( t_sec!( "Variant" ).size( 13.0 ) );
	col = col.push(
		row::<Msg>()
			.spacing( 6.0 )
			.push( wp_variant_btn( "Light variant", ThemeMode::Light, state.wallpaper_variant ) )
			.push( wp_variant_btn( "Dark variant",  ThemeMode::Dark,  state.wallpaper_variant ) )
	);
	col = col.push( section_sep() );
	col = col.push(
		t_sec!( "Additional wallpapers can be installed in the active theme's branding/ directory." )
			.size( 12.0 )
	);

	col.push( spacer() ).into()
}

fn wp_variant_btn( label: &str, value: ThemeMode, current: ThemeMode ) -> Element<Msg>
{
	button( label )
		.variant( if value == current { ButtonVariant::Primary } else { ButtonVariant::Secondary } )
		.on_press( Msg::SetWallpaperVariant( value ) )
		.into()
}

// ── Themes ───────────────────────────────────────────────────────────────────

fn view_themes( state: &State ) -> Element<Msg>
{
	use chrono::Timelike as _;
	let hour    = chrono::Local::now().hour();
	let active  = state.theme_pref.resolve( hour );
	let mode_lbl = match active
	{
		ThemeMode::Light => "Light",
		ThemeMode::Dark  => "Dark",
	};
	let pref_lbl = match state.theme_pref
	{
		ThemePreference::Auto  => "Auto",
		ThemePreference::Light => "Light",
		ThemePreference::Dark  => "Dark",
	};
	let summary = format!( "Preference: {} (currently {})", pref_lbl, mode_lbl );

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 12.0 )
		.push( header( "Themes" ) )
		.push( t_sec!( "Appearance" ).size( 13.0 ) )
		.push(
			row::<Msg>()
				.spacing( 6.0 )
				.push( theme_btn( "Auto",  ThemePreference::Auto,  state.theme_pref ) )
				.push( theme_btn( "Light", ThemePreference::Light, state.theme_pref ) )
				.push( theme_btn( "Dark",  ThemePreference::Dark,  state.theme_pref ) )
		)
		.push( section_sep() )
		.push( t_sec!( summary ).size( 13.0 ) )
		.push( t_sec!( "Auto follows the time of day (dark 19:00–07:00)." ).size( 12.0 ) )
		.push( spacer() )
		.into()
}

fn theme_btn( label: &str, value: ThemePreference, current: ThemePreference ) -> Element<Msg>
{
	button( label )
		.variant( if value == current { ButtonVariant::Primary } else { ButtonVariant::Secondary } )
		.on_press( Msg::SetThemePref( value ) )
		.into()
}

// ── Fingerprint ──────────────────────────────────────────────────────────────

fn view_fingerprint( state: &State ) -> Element<Msg>
{
	let fprint_status = match (state.fprintd_active, state.fprintd_enrolled) {
		(false, _)    => "Service unavailable",
		(true, false) => "Not enrolled",
		(true, true)  => "Enrolled",
	};

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Fingerprint, Facial data, and Screen lock" ) )
		.push( list_item( "Fingerprint daemon" )
			.trailing( if state.fprintd_active { "Active" } else { "Inactive" } ) )
		.push( list_item( "Fingerprint" ).trailing( fprint_status ) )
		.push( list_item( "Face unlock" ).trailing( "Not enrolled" ) )
		.push( spacer() )
		.into()
}

// ── Security and Emergencies ─────────────────────────────────────────────────

fn view_security_emergencies( state: &State ) -> Element<Msg>
{
	let fw_str = if state.firewall_active { "Active" } else { "Inactive" };
	let se_str = state.selinux_state.as_deref().unwrap_or( "Not available" );

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Security and Emergencies" ) )
		.push( t_sec!( "System security" ).size( 13.0 ) )
		.push( list_item( "Firewall" ).trailing( fw_str ) )
		.push( list_item( "SELinux" ).trailing( se_str ) )
		.push( separator() )
		.push( t_sec!( "Emergency" ).size( 13.0 ) )
		.push( list_item( "Emergency SOS" ).trailing( "Call 112" ) )
		.push( spacer() )
		.into()
}

// ── Privacy Protection ───────────────────────────────────────────────────────

fn view_privacy_protection( state: &State ) -> Element<Msg>
{
	let fw_str = if state.firewall_active { "Active" } else { "Inactive" };
	let se_str = state.selinux_state.as_deref().unwrap_or( "Not available" );

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Privacy Protection" ) )
		.push( list_item( "Firewall" ).trailing( fw_str ) )
		.push( list_item( "SELinux" ).trailing( se_str ) )
		.push( separator() )
		.push(
			toggle( state.location_enabled )
				.label( "Location Services" )
				.on_toggle( Msg::ToggleLocation )
		)
		.push( spacer() )
		.into()
}

// ── Apps ─────────────────────────────────────────────────────────────────────

fn view_apps( state: &State ) -> Element<Msg>
{
	let mut col = column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Apps" ) );

	if state.installed_apps.is_empty()
	{
		col = col.push( t_sec!( "Loading installed apps…" ).size( 14.0 ) );
	}
	else
	{
		let mut app_col = column::<Msg>().spacing( 0.0 );
		for app in &state.installed_apps
		{
			let exec = app.exec.clone();
			app_col = app_col.push(
				list_item( &app.name )
					.trailing( "›" )
					.on_press( Msg::LaunchApp( exec ) )
			);
		}
		col = col.push( scroll( app_col ) );
	}

	col.push( spacer() ).into()
}

// ── Battery ──────────────────────────────────────────────────────────────────

const BATTERY_FULL_COLOR:   Color = Color::hex( 0x34, 0xC7, 0x59 );
const BATTERY_LOW_COLOR:    Color = Color::hex( 0xFF, 0x95, 0x00 );
const BATTERY_CRIT_COLOR:   Color = Color::hex( 0xFF, 0x3B, 0x30 );
const BATTERY_CHARGE_COLOR: Color = Color::hex( 0x00, 0x7A, 0xFF );

fn view_battery( state: &State ) -> Element<Msg>
{
	let palette = ltk::theme_palette();

	if !state.battery_present
	{
		return column::<Msg>()
			.padding( 24.0 )
			.spacing( 16.0 )
			.push( header( "Battery" ) )
			.push( t_sec!( "No battery detected" ).size( 14.0 ).align_center() )
			.push( spacer() )
			.into();
	}

	let pct       = state.battery_pct.unwrap_or( 0 );
	let used_frac = pct as f32 / 100.0;
	let level_color =
		if state.battery_chg  { BATTERY_CHARGE_COLOR }
		else if pct >= 50     { BATTERY_FULL_COLOR    }
		else if pct >= 20     { BATTERY_LOW_COLOR     }
		else                  { BATTERY_CRIT_COLOR    };

	let status = match ( state.battery_chg, state.ac_online )
	{
		( true,  _     ) => "Charging",
		( false, true  ) => "Plugged in, not charging",
		( false, false ) => "On battery",
	};

	let time_line = state.battery_time_secs.map( |s|
	{
		let abs   = s.abs();
		let hours = abs / 3600;
		let mins  = ( abs % 3600 ) / 60;
		let label = if s > 0 { "until empty" } else { "until full" };
		if hours > 0 { format!( "{}h {}m {}", hours, mins, label ) }
		else         { format!( "{}m {}", mins, label ) }
	} );

	let mut col = column::<Msg>()
		.padding( 24.0 )
		.spacing( 12.0 )
		.push( header( "Battery" ) )
		.push( t!( format!( "{}%", pct ) ).size( 56.0 ).align_center() )
		.push( t_sec!( status ).size( 15.0 ).align_center() );

	if let Some( tl ) = time_line
	{
		col = col.push( t_sec!( tl ).size( 13.0 ).align_center() );
	}

	let bar_w = 80.0;
	let bar_h = 24.0_f32;
	col = col.push(
		row::<Msg>()
			.push( seg( used_frac         * bar_w, bar_h, level_color        ) )
			.push( seg( ( 1.0 - used_frac ) * bar_w, bar_h, palette.surface_alt ) )
	);
	col = col.push( legend_row(
		if state.battery_chg { "Charging" } else { "Used" },
		&format!( "{}%", pct ),
		level_color,
	) );
	col = col.push( legend_row( "Free", &format!( "{}%", 100u8.saturating_sub( pct ) ), palette.surface_alt ) );

	// ── Power Mode ──────────────────────────────────────────────────────
	col = col.push( section_sep() );
	col = col.push( t_sec!( "Power Mode" ).size( 13.0 ) );
	col = col.push(
		row::<Msg>()
			.spacing( 6.0 )
			.push( pwr_btn( "Power Save",  "power-saver", &state.power_profile ) )
			.push( pwr_btn( "Balanced",    "balanced",    &state.power_profile ) )
			.push( pwr_btn( "Performance", "performance", &state.power_profile ) )
	);

	// ── Health ──────────────────────────────────────────────────────────
	col = col.push( section_sep() );
	col = col.push( t_sec!( "Battery Health" ).size( 13.0 ) );
	if let ( Some( design ), Some( now ) ) = ( state.battery_full_design, state.battery_full )
	{
		let ratio_pct = if design > 0 { ( now * 100 / design ) as u32 } else { 100 };
		col = col.push( t_sec!( format!( "Maximum capacity: {}%", ratio_pct ) ).size( 13.0 ) );
		col = col.push( progress_bar( ratio_pct as f32 / 100.0 ) );
	}
	else
	{
		col = col.push( t_sec!( "Capacity data unavailable" ).size( 13.0 ) );
	}
	if let Some( c ) = state.battery_cycles
	{
		col = col.push( t_sec!( format!( "Cycle count: {}", c ) ).size( 13.0 ) );
	}

	col = col.push( section_sep() );
	col = col.push(
		t_sec!( if state.ac_online { "Power source: AC adapter" } else { "Power source: Battery" } )
			.size( 13.0 )
	);

	col.push( spacer() ).into()
}

fn pwr_btn( label: &str, value: &'static str, current: &str ) -> Element<Msg>
{
	button( label )
		.variant( if current == value { ButtonVariant::Primary } else { ButtonVariant::Secondary } )
		.on_press( Msg::SetPowerProfile( value.to_string() ) )
		.into()
}

// ── Digital Wellbeing ────────────────────────────────────────────────────────

fn view_digital_wellbeing( state: &State ) -> Element<Msg>
{
	let uptime     = fmt_uptime( state.uptime_secs );
	let mem_used   = storage::fmt_bytes( state.used_memory );
	let mem_total  = storage::fmt_bytes( state.total_memory );
	let mem_frac   = ( state.used_memory as f32 / state.total_memory.max( 1 ) as f32 ).clamp( 0.0, 1.0 );

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Digital Wellbeing and Parental Control" ) )
		.push( list_item( "System uptime" ).trailing( &uptime ) )
		.push( separator() )
		.push( t_sec!( "Memory" ).size( 13.0 ) )
		.push( t_sec!( format!( "{} used of {}", mem_used, mem_total ) ).size( 13.0 ) )
		.push( progress_bar( mem_frac ) )
		.push( spacer() )
		.into()
}

// ── Account ──────────────────────────────────────────────────────────────────

fn view_account( state: &State ) -> Element<Msg>
{
	column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Account" ) )
		.push( list_item( "Username" ).trailing( &state.username ) )
		.push( list_item( "Device name" ).trailing( &state.hostname ) )
		.push( separator() )
		.push( list_item( "Eydos account" ).trailing( "Not signed in" ) )
		.push( spacer() )
		.into()
}

// ── Privacy ──────────────────────────────────────────────────────────────────

fn view_privacy( state: &State ) -> Element<Msg>
{
	let fw_str = if state.firewall_active { "Active" } else { "Inactive" };

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 8.0 )
		.push( header( "Privacy" ) )
		.push(
			toggle( state.location_enabled )
				.label( "Location Services" )
				.on_toggle( Msg::ToggleLocation )
		)
		.push( separator() )
		.push( list_item( "Firewall" ).trailing( fw_str ) )
		.push( spacer() )
		.into()
}

// ── Location ─────────────────────────────────────────────────────────────────

fn view_location( state: &State ) -> Element<Msg>
{
	column::<Msg>()
		.padding( 24.0 )
		.spacing( 8.0 )
		.push( header( "Location" ) )
		.push(
			toggle( state.location_enabled )
				.label( "Location Services" )
				.on_toggle( Msg::ToggleLocation )
		)
		.push( spacer() )
		.into()
}

// ── Storage ──────────────────────────────────────────────────────────────────

fn view_storage( state: &State ) -> Element<Msg>
{
	let mut col = column::<Msg>()
		.padding( 24.0 )
		.spacing( 12.0 )
		.push( header( "Storage" ) );

	if let Some( disk ) = state.storage.first()
	{
		let used_frac = ( disk.used as f32 / disk.total.max( 1 ) as f32 ).clamp( 0.0, 1.0 );
		let free      = disk.total.saturating_sub( disk.used );

		col = col.push(
			t_sec!( format!(
				"{} used of {}",
				storage::fmt_bytes( disk.used ),
				storage::fmt_bytes( disk.total )
			) ).size( 13.0 )
		);
		col = col.push( progress_bar( used_frac ) );
		col = col.push( separator() );
		col = col.push( list_item( "Used"  ).trailing( &storage::fmt_bytes( disk.used ) ) );
		col = col.push( list_item( "Free"  ).trailing( &storage::fmt_bytes( free ) ) );
		col = col.push( list_item( "Total" ).trailing( &storage::fmt_bytes( disk.total ) ) );
		col = col.push( separator() );
		col = col.push( list_item( "Mount point" ).trailing( &disk.mount ) );
	}
	else
	{
		col = col.push( t_sec!( "No storage info available" ).size( 14.0 ) );
	}

	col.push( spacer() ).into()
}

// ── Region & Language ────────────────────────────────────────────────────────

fn view_language( _state: &State ) -> Element<Msg>
{
	let locale = std::env::var( "LANG" ).unwrap_or_else( |_| "en_US.UTF-8".into() );
	let lang   = locale.split( '.' ).next().unwrap_or( &locale ).to_string();

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Region & Language" ) )
		.push( list_item( "Language" ).trailing( &lang ) )
		.push( list_item( "Locale" ).trailing( &locale ) )
		.push( spacer() )
		.into()
}

// ── Date & Time ──────────────────────────────────────────────────────────────

fn view_datetime( _state: &State ) -> Element<Msg>
{
	use chrono::Local;
	let now      = Local::now();
	let date_str = now.format( "%Y-%m-%d" ).to_string();
	let time_str = now.format( "%H:%M:%S" ).to_string();
	let tz_str   = std::fs::read_to_string( "/etc/timezone" )
		.map( |s| s.trim().to_string() )
		.unwrap_or_else( |_| "UTC".into() );

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 4.0 )
		.push( header( "Date & Time" ) )
		.push( list_item( "Date" ).trailing( &date_str ) )
		.push( list_item( "Time" ).trailing( &time_str ) )
		.push( list_item( "Time zone" ).trailing( &tz_str ) )
		.push( spacer() )
		.into()
}

// ── Accessibility ────────────────────────────────────────────────────────────

fn view_accessibility( state: &State ) -> Element<Msg>
{
	let scale_pct = ( state.ui_scale * 100.0 ).round() as u32;
	let scale_norm = ( state.ui_scale - 1.0 ) / 1.0; // 1.0..2.0 → 0..1

	column::<Msg>()
		.padding( 24.0 )
		.spacing( 8.0 )
		.push( header( "Accessibility" ) )
		.push( t_sec!( format!( "Text scale: {}%", scale_pct ) ).size( 13.0 ) )
		.push(
			slider( scale_norm.clamp( 0.0, 1.0 ) )
				.on_change( |v| Msg::SetUiScale( 1.0 + v ) )
		)
		.push( spacer() )
		.into()
}

// ── Shared widget helpers ─────────────────────────────────────────────────────

fn seg( width_vw: f32, height_px: f32, color: Color ) -> Element<Msg>
{
	container(
		spacer()
			.width(  Length::vw( width_vw ) )
			.height( Length::px( height_px ) )
	)
	.background( color )
	.radius( 4.0 )
	.into()
}

fn legend_row( label: &str, value: &str, color: Color ) -> Element<Msg>
{
	row::<Msg>()
		.spacing( 8.0 )
		.push( ltk::text( "●" ).color( color ).size( 18.0 ) )
		.push( t!( label ).size( 14.0 ) )
		.push( spacer() )
		.push( t_sec!( value ).size( 14.0 ) )
		.into()
}

// ── Entry point ───────────────────────────────────────────────────────────────

fn main()
{
	tracing_subscriber::fmt()
		.with_env_filter( tracing_subscriber::EnvFilter::from_default_env() )
		.init();

	let args = std::env::args().collect::<Vec<_>>();
	// Command-line flags override the persisted preference for this run.
	let pref = if args.iter().any( |a| a == "--dark"  ) { ThemePreference::Dark  }
	      else if args.iter().any( |a| a == "--light" ) { ThemePreference::Light }
	      else                                          { config::read_theme_pref() };
	use chrono::Timelike as _;
	let hour = chrono::Local::now().hour();
	let mode: ThemeMode = pref.resolve( hour );

	match ltk::ThemeDocument::find( "default" )
	{
		Ok( doc ) =>
		{
			ltk::set_active_document( doc );
			ltk::set_active_mode( mode );
			tracing::info!( "settings theme: default / {:?} (preference: {:?})", mode, pref );
		}
		Err( e ) =>
		{
			tracing::warn!(
				"default theme not loadable ({e}); falling back to embedded \
				 B/W. Install ltk-theme-default or set LTK_THEMES_DIR.",
			);
			ltk::set_active_mode( mode );
		}
	}

	ltk::run( State::new() );
}
