use std::process::Command;

/// Generate a random 8-character alphanumeric password for the hotspot
/// using /dev/urandom for cryptographic quality.
fn random_password() -> String
{
	let chars = b"abcdefghijklmnopqrstuvwxyz0123456789";
	let mut bytes = [0u8; 8];
	if std::fs::File::open( "/dev/urandom" )
		.and_then( |mut f| {
			use std::io::Read;
			f.read_exact( &mut bytes )
		} )
		.is_err()
	{
		tracing::error!( "Failed to read /dev/urandom for hotspot password" );
		return "changeme1".to_string();
	}
	bytes.iter().map( |&b| chars[b as usize % chars.len()] as char ).collect()
}

fn spawn_logged( cmd: &mut Command, context: &str )
{
	match cmd.spawn()
	{
		Ok( _ ) => {}
		Err( e ) => tracing::error!( "Failed to {}: {}", context, e ),
	}
}

#[ allow( dead_code ) ]
#[derive( Debug, Clone, Copy, PartialEq, Eq, Hash )]
pub enum ToggleId
{
	Flashlight,
	NightLight,
	DoNotDisturb,
	DarkMode,
	Location,
	Hotspot,
	Vpn,
	MobileData,
	Caffeine,
	RotationLock,
	/// Surfaced in the QS top button bank, not in the bottom toggle grid.
	/// The radio kill is unimplemented — `toggle_airplane_mode` only flips
	/// the in-memory flag so the UI can render pressed / idle state.
	AirplaneMode,
}

#[derive( Debug, Clone, Default )]
pub struct ToggleState
{
	pub flashlight: bool,
	pub night_light: bool,
	pub dnd: bool,
	pub dark_mode: bool,
	pub location: bool,
	pub hotspot: bool,
	pub vpn: bool,
	pub mobile_data: bool,
	pub caffeine: bool,
	pub rotation_lock: bool,
	pub airplane_mode: bool,
}

impl ToggleState
{
	pub fn toggle( &mut self, id: ToggleId )
	{
		match id
		{
			ToggleId::Flashlight    => self.toggle_flashlight(),
			ToggleId::NightLight    => self.toggle_night_light(),
			ToggleId::DoNotDisturb  => self.toggle_dnd(),
			ToggleId::DarkMode      => self.toggle_dark_mode(),
			ToggleId::Location      => self.toggle_location(),
			ToggleId::Hotspot       => self.toggle_hotspot(),
			ToggleId::Vpn           => self.toggle_vpn(),
			ToggleId::MobileData    => self.toggle_mobile_data(),
			ToggleId::Caffeine      => self.toggle_caffeine(),
			ToggleId::RotationLock  => self.toggle_rotation_lock(),
			ToggleId::AirplaneMode  => self.toggle_airplane_mode(),
		}
	}

	fn toggle_airplane_mode( &mut self )
	{
		self.airplane_mode = !self.airplane_mode;
		tracing::info!( "AirplaneMode toggled (no-op): {}", self.airplane_mode );
	}

	fn toggle_flashlight( &mut self )
	{
		self.flashlight = !self.flashlight;
		let val = if self.flashlight { "1" } else { "0" };
		if let Err( e ) = std::fs::write( "/sys/class/leds/white:flash/brightness", val )
		{
			tracing::error!( "Failed to set flashlight: {}", e );
		}
	}

	fn toggle_night_light( &mut self )
	{
		self.night_light = !self.night_light;
		if self.night_light
		{
			spawn_logged(
				Command::new( "sh" ).args( ["-c", "pkill wlsunset; wlsunset -t 3500 -T 6500 &"] ),
				"start wlsunset",
			);
		} else {
			spawn_logged(
				Command::new( "pkill" ).args( ["wlsunset"] ),
				"stop wlsunset",
			);
		}
	}

	fn toggle_dnd( &mut self )
	{
		self.dnd = !self.dnd;
		let profile = if self.dnd { "silent" } else { "full" };
		spawn_logged(
			Command::new( "dbus-send" ).args( [
				"--session",
				"--type=method_call",
				"--dest=org.sigxcpu.Feedback",
				"/org/sigxcpu/Feedback",
				"org.freedesktop.DBus.Properties.Set",
				"string:org.sigxcpu.Feedback",
				"string:Profile",
				&format!( "variant:string:{}", profile ),
			] ),
			"set DND profile",
		);
	}

	fn toggle_dark_mode( &mut self )
	{
		self.dark_mode = !self.dark_mode;
		let scheme = if self.dark_mode { "prefer-dark" } else { "default" };
		spawn_logged(
			Command::new( "gsettings" )
				.args( ["set", "org.gnome.desktop.interface", "color-scheme", scheme] ),
			"set dark mode",
		);
	}

	fn toggle_location( &mut self )
	{
		self.location = !self.location;
		let val = if self.location { "true" } else { "false" };
		spawn_logged(
			Command::new( "gsettings" )
				.args( ["set", "org.gnome.system.location", "enabled", val] ),
			"set location",
		);
	}

	fn toggle_hotspot( &mut self )
	{
		self.hotspot = !self.hotspot;
		if self.hotspot
		{
			let pw = random_password();
			tracing::info!( "Hotspot password: {}", pw );
			spawn_logged(
				Command::new( "nmcli" ).args( [
					"device", "wifi", "hotspot", "ifname", "wlan0",
					"ssid", "Crustace-Hotspot", "password", &pw,
				] ),
				"start hotspot",
			);
		} else {
			spawn_logged(
				Command::new( "sh" ).args( [
					"-c",
					"nmcli connection down Hotspot 2>/dev/null; \
					 nmcli connection delete Hotspot 2>/dev/null",
				] ),
				"stop hotspot",
			);
		}
	}

	fn toggle_vpn( &mut self )
	{
		self.vpn = !self.vpn;
		if self.vpn
		{
			if let Ok( out ) = Command::new( "nmcli" )
				.args( ["-t", "-f", "NAME,TYPE", "connection", "show"] )
				.output()
			{
				let stdout = String::from_utf8_lossy( &out.stdout );
				for line in stdout.lines()
				{
					if line.contains( "vpn" ) || line.contains( "wireguard" )
					{
						if let Some( name ) = line.split( ':' ).next()
						{
							spawn_logged(
								Command::new( "nmcli" ).args( ["connection", "up", name] ),
								"connect VPN",
							);
							break;
						}
					}
				}
			}
		} else {
			spawn_logged(
				Command::new( "sh" ).args( [
					"-c",
					"nmcli -t -f NAME,TYPE connection show --active | \
					 grep -E 'vpn|wireguard' | cut -d: -f1 | \
					 xargs -I{} nmcli connection down '{}'",
				] ),
				"disconnect VPN",
			);
		}
	}

	fn toggle_mobile_data( &mut self )
	{
		self.mobile_data = !self.mobile_data;
		if self.mobile_data
		{
			spawn_logged(
				Command::new( "nmcli" ).args( ["connection", "up", "gsm"] ),
				"enable mobile data",
			);
		} else {
			spawn_logged(
				Command::new( "nmcli" ).args( ["connection", "down", "gsm"] ),
				"disable mobile data",
			);
		}
	}

	fn toggle_caffeine( &mut self )
	{
		self.caffeine = !self.caffeine;
		if self.caffeine
		{
			spawn_logged(
				Command::new( "sh" ).args( [
					"-c",
					"systemd-inhibit --what=idle --who=crustace-caffeine \
					 --why='Caffeine mode' sleep infinity &",
				] ),
				"enable caffeine mode",
			);
		} else {
			spawn_logged(
				Command::new( "pkill" ).args( ["-f", "crustace-caffeine"] ),
				"disable caffeine mode",
			);
		}
	}

	fn toggle_rotation_lock( &mut self )
	{
		self.rotation_lock = !self.rotation_lock;
		spawn_logged(
			Command::new( "dbus-send" ).args( [
				"--session",
				"--type=method_call",
				"--dest=org.gnome.ScreenSaver",
				"/org/gnome/ScreenSaver",
				"org.freedesktop.DBus.Properties.Set",
				"string:org.gnome.ScreenSaver",
				"string:RotationLock",
				&format!( "variant:boolean:{}", self.rotation_lock ),
			] ),
			"set rotation lock via D-Bus",
		);
		spawn_logged(
			Command::new( "gsettings" ).args( [
				"set",
				"org.gnome.settings-daemon.peripherals.touchscreen",
				"orientation-lock",
				if self.rotation_lock { "true" } else { "false" },
			] ),
			"set rotation lock via gsettings",
		);
	}

}
