//! Simple service that does nothing other than log/trace every N seconds.

mod argp;
mod err;
mod procres;

use std::time::{Duration, Instant};

use tokio::sync::mpsc;

use qsu::{
  argp::ArgParser,
  rt::{InitCtx, RunEnv, SrvAppRt, SvcEvt, TermCtx, TokioServiceHandler}
};

use err::Error;
use procres::ProcRes;


struct MyService {
  rx: mpsc::UnboundedReceiver<SvcEvt>
}

#[qsu::async_trait]
impl TokioServiceHandler for MyService {
  type AppErr = Error;

  async fn init(&mut self, ictx: &mut InitCtx) -> Result<(), Self::AppErr> {
    ictx.report(Some("Entered init"));
    Ok(())
  }

  #[allow(clippy::redundant_pub_crate)]
  async fn run(&mut self, _re: &RunEnv) -> Result<(), Self::AppErr> {
    const SECS: u64 = 30;

    // unwrap() is okay due to time scales involed
    let mut last_dump = Instant::now()
      .checked_sub(Duration::from_secs(SECS))
      .unwrap();
    #[expect(clippy::needless_continue)]
    loop {
      if last_dump.elapsed() > Duration::from_secs(SECS) {
        log::error!("error");
        log::warn!("warn");
        log::info!("info");
        log::debug!("debug");
        log::trace!("trace");

        tracing::error!("error");
        tracing::warn!("warn");
        tracing::info!("info");
        tracing::debug!("debug");
        tracing::trace!("trace");

        last_dump = Instant::now();
      }
      tokio::select! {
        () = tokio::time::sleep(std::time::Duration::from_secs(1)) => {
          continue;
        }
        evt = self.rx.recv() => {
          match evt {
            Some(SvcEvt::Shutdown(_)) => {
              tracing::info!("The service subsystem requested that the application shut down");
              break;
            }
            Some(SvcEvt::ReloadConf) => {
              tracing::info!("The service subsystem requested that application reload configuration");
            }
            _ => { }
          }
        }
      }
    }

    Ok(())
  }

  async fn shutdown(
    &mut self,
    tctx: &mut TermCtx
  ) -> Result<(), Self::AppErr> {
    tctx.report(Some("Entered shutdown"));
    tracing::trace!("Running shutdown()");
    Ok(())
  }
}


fn main() -> ProcRes {
  // In the future we'll be able to use Try to implement support for implicit
  // conversion to ProcRes from a Result using `?`, but for now use this hack.
  ProcRes::into(main2().into())
}

fn main2() -> Result<(), Error> {
  // Derive default service name from executable name.
  let svcname = qsu::default_service_name()
    .expect("Unable to determine default service name");

  let bldr = Box::new(|| {
    let (tx, rx) = mpsc::unbounded_channel();
    let svcevt_handler = Box::new(move |msg| {
      // Just foward messages to runtime handler
      tx.send(msg).unwrap();
    });
    let rt_handler = Box::new(MyService { rx });
    SrvAppRt::Tokio {
      rtbldr: None,
      svcevt_handler,
      rt_handler
    }
  });

  // Parse, and process, command line arguments.
  let mut argsproc = argp::AppArgsProc { bldr };
  let ap = ArgParser::new(&svcname, &mut argsproc);
  ap.proc()?;

  Ok(())
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :
