// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::future::Future;

use tokio::{task::JoinHandle, sync::oneshot::{self, Receiver}};

pub struct Task<T>(Option<RunningTask<T>>);

impl<T> Task<T> where T: Send + 'static {
	pub fn new() -> Task<T> { Task(None) }

	pub fn run(future: impl Future<Output = Result<T, String>> + Send + 'static) -> Task<T> {
		let (sender, result) = oneshot::channel();
		Task(Some(RunningTask {
			handle: tokio::spawn(async {
				let _ = sender.send(future.await);
			}),
			result,
		}))
	}

	pub fn empty(&self) -> bool { self.0.is_none() }
	pub fn running(&self) -> bool { self.0.is_some() }

	pub fn set(&mut self, future: impl Future<Output = Result<T, String>> + Send + 'static) {
		*self = Task::run(future);
	}

	pub fn stop(&mut self) {
		*self = Task::new();
	}

	pub fn result(&mut self) -> Option<Result<T, String>> {
		let res = self.0.as_mut()?.result.try_recv().ok()?;
		self.stop();
		Some(res)
	}
}

struct RunningTask<T> {
	handle: JoinHandle<()>,
	result: Receiver<Result<T, String>>,
}

impl<T> Drop for RunningTask<T> {
	fn drop(&mut self) {
		/*
		 * Want tasks to stop once their return value is lost, to prevent dropped
		 * connections from continuing.
		 */
		self.handle.abort();
	}
}
