//! Greeter supervisor: at daemon startup, forks the configured greeter
//! command as the configured user on a dedicated VT, then waits and
//! respawns on exit. Uses the same PAM + fork machinery as user sessions
//! but skips `authenticate()` — the greeter is a system service, not a
//! user login. `pam_open_session` still runs in the child so logind sets
//! up `/run/user/$UID` and `XDG_RUNTIME_DIR` for forge.

use anyhow::{ anyhow, Result };
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Notify;
use tokio::task;

use crate::config::GreeterEntry;
use crate::pam_session::{ ConversationHandler, Transaction };
use crate::server::{ SessionState, SharedSessionState };
use crate::session;
use crate::vt;

const PAM_SERVICE:  &str = "loginmanager-greeter";
const RESPAWN_GAP:  Duration = Duration::from_millis( 500 );
const ERROR_GAP:    Duration = Duration::from_secs( 5 );

struct NoopHandler;

impl ConversationHandler for NoopHandler
{
	fn prompt_blind( &mut self, _: &str ) -> Option<String> { None }
	fn prompt_echo(  &mut self, _: &str ) -> Option<String> { None }
}

pub async fn supervise( cfg: GreeterEntry, state: SharedSessionState, respawn: Arc<Notify> )
{
	let mut first = true;
	loop
	{
		match spawn_once( cfg.clone(), first ).await
		{
			Ok( () ) =>
			{
				tracing::info!( "greeter: exited" );

				// If a user session is in flight, the daemon will eventually
				// switch the VT back to the greeter's VT and call notify_one().
				// Spawning the greeter before that means forge starts on a
				// non-foreground VT, libseat keeps the session paused, and the
				// resume on `session activate` never fires cleanly — same
				// bug that motivated killing the greeter on login.
				loop
				{
					let busy = {
						let s = state.lock().await;
						!matches!( *s, SessionState::Idle )
					};
					if !busy { break; }
					tracing::info!(
						"greeter: user session active, deferring respawn until \
						 the daemon switches back to the greeter VT"
					);
					respawn.notified().await;
				}

				tracing::info!( "greeter: respawning" );
				tokio::time::sleep( RESPAWN_GAP ).await;
			}
			Err( e ) =>
			{
				tracing::error!( "greeter: spawn failed: {e}, retrying in {}s", ERROR_GAP.as_secs() );
				tokio::time::sleep( ERROR_GAP ).await;
				continue;
			}
		}
		first = false;
	}
}

async fn spawn_once( cfg: GreeterEntry, activate: bool ) -> Result<()>
{
	let user = session::lookup_user( &cfg.user )?;
	let tty_path = format!( "/dev/tty{}", cfg.vt );

	let user_name = cfg.user.clone();
	let command:  PathBuf = cfg.command;
	let target_vt = cfg.vt;
	let env       = cfg.environment;

	task::spawn_blocking( move || -> Result<()>
	{
		let mut tx = Transaction::start( PAM_SERVICE, &user_name, Box::new( NoopHandler ) )?;
		tx.set_tty( &tty_path )?;

		let pid = session::fork_session( tx, user, target_vt, &command, &[], &env )?;
		tracing::info!( "greeter: forked pid {pid} on VT {target_vt}" );

		if activate
		{
			vt::activate( target_vt )?;
			vt::wait_active( target_vt )?;
			tracing::info!( "greeter: VT {target_vt} active" );
		}

		let _ = session::wait_session( pid )?;
		Ok( () )
	} ).await.map_err( |e| anyhow!( "greeter task join: {e}" ) )??;

	Ok( () )
}
