use std::process::Command;
use std::sync::LazyLock;
use std::sync::Mutex;
use std::sync::mpsc::Sender;

use super::throttled_cmd;

const BACKLIGHT_PATHS: &[&str] = &[
	"/sys/class/backlight/intel_backlight",
	"/sys/class/backlight/amdgpu_bl0",
	"/sys/class/backlight/acpi_video0",
	"/sys/class/backlight/backlight",
];

pub fn get_brightness() -> u8
{
	let max = get_max_brightness().unwrap_or( 100 );
	let current = get_current_brightness().unwrap_or( max );

	if max > 0
	{
		( ( current as f32 / max as f32 ) * 100.0 ) as u8
	} else {
		100
	}
}

fn get_current_brightness() -> Option<u32>
{
	for base in BACKLIGHT_PATHS
	{
		let path = format!( "{}/brightness", base );
		if let Ok( content ) = std::fs::read_to_string( &path )
		{
			if let Ok( val ) = content.trim().parse::<u32>()
			{
				return Some( val );
			}
		}
	}

	let output = Command::new( "brightnessctl" ).args( ["get"] ).output().ok()?;
	if output.status.success()
	{
		let stdout = String::from_utf8_lossy( &output.stdout );
		return stdout.trim().parse().ok();
	}

	None
}

fn get_max_brightness() -> Option<u32>
{
	for base in BACKLIGHT_PATHS
	{
		let path = format!( "{}/max_brightness", base );
		if let Ok( content ) = std::fs::read_to_string( &path )
		{
			if let Ok( val ) = content.trim().parse::<u32>()
			{
				return Some( val );
			}
		}
	}

	let output = Command::new( "brightnessctl" ).args( ["max"] ).output().ok()?;
	if output.status.success()
	{
		let stdout = String::from_utf8_lossy( &output.stdout );
		return stdout.trim().parse().ok();
	}

	None
}

/// Hot path for slider drags / hardware key presses. Pushes the new
/// percent onto the worker channel; the worker collapses bursts to the
/// most recent value and applies it. See [`super::throttled_cmd`] for
/// the full rationale.
pub fn set_brightness( pct: u8 )
{
	let _ = BRIGHTNESS_TX.lock().unwrap().send( pct );
}

/// Cached writable sysfs backlight path resolved on first use. `Some`
/// means direct `write()` works (no fork, no DBus, no PWM ramp arbitration
/// from logind — typically <1 ms per change vs ~30-50 ms for a
/// `brightnessctl` spawn that may go through systemd-logind). `None`
/// means we already tried and either no path exists or none was
/// writable, so we permanently fall back to the `brightnessctl`
/// subprocess for this session.
struct DirectSysfs
{
	brightness_path: std::path::PathBuf,
	max_raw:         u32,
}

static DIRECT_SYSFS: LazyLock<Option<DirectSysfs>> = LazyLock::new( ||
{
	for base in BACKLIGHT_PATHS
	{
		let bpath = std::path::PathBuf::from( format!( "{}/brightness",     base ) );
		let mpath = std::path::PathBuf::from( format!( "{}/max_brightness", base ) );
		let max_raw = std::fs::read_to_string( &mpath ).ok()
			.and_then( | s | s.trim().parse::<u32>().ok() );
		let Some( max_raw ) = max_raw else { continue };
		if max_raw == 0 { continue; }
		// Probe write permission once with the value already there, so
		// we do not perturb the current brightness.
		let probe = std::fs::read_to_string( &bpath ).ok()
			.and_then( | s | s.trim().parse::<u32>().ok() )
			.unwrap_or( max_raw );
		if std::fs::write( &bpath, probe.to_string() ).is_err() { continue; }
		return Some( DirectSysfs { brightness_path: bpath, max_raw } );
	}
	None
} );

static BRIGHTNESS_TX: LazyLock<Mutex<Sender<u8>>> = LazyLock::new( ||
{
	Mutex::new( throttled_cmd::spawn_worker( | pct: u8 |
	{
		if let Some( ref sysfs ) = *DIRECT_SYSFS
		{
			let target = ( pct as u32 * sysfs.max_raw + 50 ) / 100;
			if let Err( e ) = std::fs::write( &sysfs.brightness_path, target.to_string() )
			{
				tracing::error!( "Direct sysfs brightness write failed: {}", e );
			}
			return;
		}
		// Fallback: no writable sysfs path, defer to `brightnessctl`
		// (which routes through systemd-logind via DBus when the user
		// lacks direct sysfs permission).
		match Command::new( "brightnessctl" )
			.args( ["set", &format!( "{}%", pct )] )
			.spawn()
		{
			Ok( mut child ) => { let _ = child.wait(); }
			Err( e )        => tracing::error!( "Failed to set brightness: {}", e ),
		}
	} ) )
} );
