use std::process::Command;
use std::fs;

use super::cmd_stdout;

pub fn hostname() -> String
{
	fs::read_to_string( "/etc/hostname" )
		.map( |s| s.trim().to_string() )
		.unwrap_or_else( |_| "eydos".into() )
}

pub fn kernel_version() -> String
{
	fs::read_to_string( "/proc/sys/kernel/osrelease" )
		.map( |s| s.trim().to_string() )
		.unwrap_or_default()
}

pub fn os_version() -> String
{
	fs::read_to_string( "/etc/os-release" ).ok()
		.and_then( |content|
		{
			for line in content.lines()
			{
				if let Some( rest ) = line.strip_prefix( "PRETTY_NAME=" )
				{
					return Some( rest.trim_matches( '"' ).to_string() );
				}
			}
			None
		})
		.unwrap_or_else( || "Eydos OS".into() )
}

pub fn cpu_info() -> ( String, usize )
{
	let content = fs::read_to_string( "/proc/cpuinfo" ).ok().unwrap_or_default();
	let model = content.lines().find_map( |l|
		l.strip_prefix( "model name" )
			.map( |r| r.trim_start_matches( ": " ).trim().to_string() )
	).unwrap_or_else( || "Unknown CPU".into() );
	let cores = content.lines().filter( |l| l.starts_with( "processor" ) ).count().max( 1 );
	( model, cores )
}

#[ allow( dead_code ) ] pub fn cpu_model() -> String { cpu_info().0 }
#[ allow( dead_code ) ] pub fn cpu_cores() -> usize  { cpu_info().1 }

pub fn memory_info() -> ( u64, u64 )
{
	let content = fs::read_to_string( "/proc/meminfo" ).ok().unwrap_or_default();
	let total = meminfo_field( &content, "MemTotal:" ).unwrap_or( 0 );
	let avail = meminfo_field( &content, "MemAvailable:" ).unwrap_or( 0 );
	( total, total.saturating_sub( avail ) )
}

#[ allow( dead_code ) ] pub fn total_memory() -> u64 { memory_info().0 }
pub fn used_memory()  -> u64 { memory_info().1 }

fn meminfo_field( s: &str, prefix: &str ) -> Option<u64>
{
	s.lines().find_map( |l|
		l.strip_prefix( prefix )?
			.trim().split_whitespace().next()?
			.parse::<u64>().ok()
			.map( |kb| kb * 1024 )
	)
}

pub fn uptime_secs() -> u64
{
	fs::read_to_string( "/proc/uptime" ).ok()
		.and_then( |s| s.split_whitespace().next()?.parse::<f64>().ok() )
		.map( |f| f as u64 ).unwrap_or( 0 )
}

pub fn primary_ip() -> Option<String>
{
	let s = cmd_stdout( "ip", &["-4", "route", "get", "1.1.1.1"] );
	let mut words = s.split_whitespace();
	while let Some( w ) = words.next()
	{
		if w == "src" { return words.next().map( |s| s.to_string() ); }
	}
	None
}

pub fn username() -> String
{
	std::env::var( "USER" ).unwrap_or_else( |_| "user".into() )
}

// ─── Updater / firewall / hotspot stub helpers ──────────────────────

pub fn updates_available() -> Option<u32>
{
	// dnf path (Fedora / Bazzite). `dnf check-update` exits 100 when
	// updates are available, 0 when none, anything else on error.
	if let Ok( out ) = Command::new( "dnf" ).arg( "check-update" ).output()
	{
		let code = out.status.code().unwrap_or( -1 );
		if code == 0 { return Some( 0 ); }
		if code == 100
		{
			let text = String::from_utf8_lossy( &out.stdout );
			let n = text.lines().filter( |l| !l.is_empty() && !l.starts_with( "Last metadata" ) && l.chars().next().map( |c| !c.is_whitespace() ).unwrap_or( false ) ).count();
			return Some( n as u32 );
		}
	}
	if let Ok( out ) = Command::new( "apt" ).args( ["list", "--upgradable"] ).output()
	{
		let text = String::from_utf8_lossy( &out.stdout );
		let n = text.lines().filter( |l| l.contains( "/" ) ).count();
		return Some( n as u32 );
	}
	None
}

pub fn firewall_active() -> bool
{
	Command::new( "firewall-cmd" ).arg( "--state" ).output().ok()
		.map( |o| o.status.success() ).unwrap_or( false ) ||
	cmd_stdout( "ufw", &["status"] ).contains( "Status: active" )
}

pub fn selinux_state() -> Option<String>
{
	Command::new( "getenforce" ).output().ok()
		.and_then( |o| String::from_utf8( o.stdout ).ok() )
		.map( |s| s.trim().to_string() )
}

// ─── Cellular / SIM (ModemManager) ──────────────────────────────────

/// Single mmcli call returning `(imei, operator_name)`.
pub fn sim_info() -> ( Option<String>, Option<String> )
{
	let text = cmd_stdout( "mmcli", &["-m", "0", "--output-keyvalue"] );
	let imei = text.lines().find_map( |l|
		l.strip_prefix( "modem.3gpp.imei" )
			.map( |r| r.trim_start_matches( " : " ).trim().to_string() )
	);
	let operator = text.lines().find_map( |l|
		l.strip_prefix( "modem.3gpp.operator-name" )
			.map( |r| r.trim_start_matches( " : " ).trim().to_string() )
	);
	( imei, operator )
}

// ─── Fingerprint daemon ──────────────────────────────────────────────

pub fn fprintd_active() -> bool
{
	Command::new( "systemctl" )
		.args( ["is-active", "--quiet", "fprintd"] )
		.status()
		.map( |s| s.success() )
		.unwrap_or( false )
}

pub fn fprintd_enrolled() -> bool
{
	let user = username();
	let s = cmd_stdout( "fprintd-list", &[ user.as_str() ] );
	let s = s.trim();
	!s.is_empty() && !s.contains( "no fingerprints" )
}

// ─── NFC ─────────────────────────────────────────────────────────────

pub fn nfc_available() -> bool
{
	fs::read_dir( "/sys/class/nfc" )
		.map( |mut d| d.next().is_some() )
		.unwrap_or( false )
}

// ─── USB mode ────────────────────────────────────────────────────────

pub fn usb_mode() -> String
{
	// Try configfs gadget first (Android-style devices).
	if let Ok( entries ) = fs::read_dir( "/sys/kernel/config/usb_gadget" )
	{
		for entry in entries.flatten()
		{
			let cfg_path = entry.path()
				.join( "configs/c.1/strings/0x409/configuration" );
			if let Ok( s ) = fs::read_to_string( &cfg_path )
			{
				let s = s.trim().to_string();
				if !s.is_empty() { return s; }
			}
		}
	}
	// Fall back to UDC state (if UDC exists the port is active).
	if let Ok( entries ) = fs::read_dir( "/sys/class/udc" )
	{
		if entries.flatten().next().is_some()
		{
			return "USB active".to_string();
		}
	}
	"Charging only".to_string()
}
