//! Greeter-mode auth: blocking client of the `loginmanager-daemon` IPC.
//! No tokio — reimplements the length-prefixed JSON framing synchronously
//! over `std` sockets and reuses `loginmanager_ipc::proto`. Run on a worker
//! thread; the App polls the result.

use std::io::{ Read, Write };
use std::os::unix::net::UnixStream;
use std::time::Duration;

use loginmanager_ipc::proto::{ Request, Response };
use loginmanager_ipc::DEFAULT_SOCKET_PATH;

fn socket_path() -> String
{
	std::env::var( "LOGINMANAGER_SOCK" ).unwrap_or_else( |_| DEFAULT_SOCKET_PATH.to_string() )
}

fn write_msg( stream: &mut UnixStream, msg: &Request ) -> Result<(), String>
{
	let body = serde_json::to_vec( msg ).map_err( |e| e.to_string() )?;
	let len  = ( body.len() as u32 ).to_le_bytes();
	stream.write_all( &len ).map_err( |e| e.to_string() )?;
	stream.write_all( &body ).map_err( |e| e.to_string() )?;
	stream.flush().map_err( |e| e.to_string() )?;
	Ok( () )
}

fn read_msg( stream: &mut UnixStream ) -> Result<Response, String>
{
	let mut len_buf = [ 0u8; 4 ];
	stream.read_exact( &mut len_buf ).map_err( |e| e.to_string() )?;
	let len = u32::from_le_bytes( len_buf ) as usize;
	let mut body = vec![ 0u8; len ];
	stream.read_exact( &mut body ).map_err( |e| e.to_string() )?;
	serde_json::from_slice( &body ).map_err( |e| e.to_string() )
}

/// Drive `BeginAuth` → challenges → `AuthAccepted` → `SessionReady`. `Ok`
/// means the daemon is spawning the session. Every secret challenge is
/// answered with the password, every other with the username.
pub fn login( username: &str, password: &str ) -> Result<(), String>
{
	let mut stream = UnixStream::connect( socket_path() ).map_err( |e| e.to_string() )?;
	stream.set_read_timeout( Some( Duration::from_secs( 45 ) ) ).ok();

	write_msg( &mut stream, &Request::BeginAuth { username: username.to_string() } )?;

	loop
	{
		match read_msg( &mut stream )?
		{
			Response::AuthChallenge { secret, .. } =>
			{
				let answer = if secret { password.to_string() } else { username.to_string() };
				write_msg( &mut stream, &Request::AuthResponse { answer: Some( answer ) } )?;
			}
			Response::AuthAccepted            => {}
			Response::AuthRejected { reason }  => return Err( reason ),
			Response::SessionReady { .. }      => return Ok( () ),
			Response::Error { description }    => return Err( description ),
		}
	}
}

pub fn power_off() -> Result<(), String>
{
	let mut stream = UnixStream::connect( socket_path() ).map_err( |e| e.to_string() )?;
	write_msg( &mut stream, &Request::PowerOff )
}

pub fn reboot() -> Result<(), String>
{
	let mut stream = UnixStream::connect( socket_path() ).map_err( |e| e.to_string() )?;
	write_msg( &mut stream, &Request::Reboot )
}
