use std::sync::Arc;

use parking_lot::Mutex;

use qsu::rt::{InitCtx, RunEnv, ServiceHandler, TermCtx};

#[cfg(feature = "tokio")]
use qsu::rt::TokioServiceHandler;


#[cfg(feature = "rocket")]
use qsu::{
  rocket::{Build, Ignite, Rocket},
  rt::RocketServiceHandler
};

use crate::err::Error;

#[derive(Default)]
pub struct FailMode {
  init: bool,
  run: bool,
  shutdown: bool
}

#[allow(unused)]
impl FailMode {
  pub const fn init(&mut self) -> &mut Self {
    self.init = true;
    self
  }
  pub const fn run(&mut self) -> &mut Self {
    self.run = true;
    self
  }
  pub const fn shutdown(&mut self) -> &mut Self {
    self.shutdown = true;
    self
  }
}

#[derive(Default)]
pub struct Visited {
  pub init: bool,
  pub run: bool,
  pub shutdown: bool
}

#[derive(Default)]
pub struct MySyncService {
  pub fail: FailMode,
  pub visited: Arc<Mutex<Visited>>
}

#[allow(unused)]
impl MySyncService {
  pub const fn fail_init(mut self) -> Self {
    self.fail.init();
    self
  }
  pub const fn fail_run(mut self) -> Self {
    self.fail.run();
    self
  }
  pub const fn fail_shutdown(mut self) -> Self {
    self.fail.shutdown();
    self
  }
}


impl ServiceHandler for MySyncService {
  type AppErr = Error;

  fn init(&mut self, _ictx: &mut InitCtx) -> Result<(), Self::AppErr> {
    self.visited.lock().init = true;
    if self.fail.init {
      Err(Error::hello("From Sync::init()"))?;
    }
    Ok(())
  }

  fn run(&mut self, _re: &RunEnv) -> Result<(), Self::AppErr> {
    self.visited.lock().run = true;
    if self.fail.run {
      Err(Error::hello("From Sync::run()"))?;
    }
    Ok(())
  }

  fn shutdown(&mut self, _tctx: &mut TermCtx) -> Result<(), Self::AppErr> {
    self.visited.lock().shutdown = true;
    if self.fail.shutdown {
      Err(Error::hello("From Sync::shutdown()"))?;
    }
    Ok(())
  }
}


#[cfg(feature = "tokio")]
#[derive(Default)]
pub struct MyTokioService {
  pub fail: FailMode,
  pub visited: Arc<Mutex<Visited>>
}

#[cfg(feature = "tokio")]
#[allow(unused)]
impl MyTokioService {
  pub const fn fail_init(mut self) -> Self {
    self.fail.init();
    self
  }
  pub const fn fail_run(mut self) -> Self {
    self.fail.run();
    self
  }
  pub const fn fail_shutdown(mut self) -> Self {
    self.fail.shutdown();
    self
  }
}

#[cfg(feature = "tokio")]
#[qsu::async_trait]
impl TokioServiceHandler for MyTokioService {
  type AppErr = Error;

  async fn init(&mut self, _ictx: &mut InitCtx) -> Result<(), Self::AppErr> {
    self.visited.lock().init = true;
    if self.fail.init {
      Err(Error::hello("From Tokio::init()"))?;
    }
    Ok(())
  }

  async fn run(&mut self, _re: &RunEnv) -> Result<(), Self::AppErr> {
    self.visited.lock().run = true;
    if self.fail.run {
      Err(Error::hello("From Tokio::run()"))?;
    }
    Ok(())
  }

  async fn shutdown(
    &mut self,
    _tctx: &mut TermCtx
  ) -> Result<(), Self::AppErr> {
    self.visited.lock().shutdown = true;
    if self.fail.shutdown {
      Err(Error::hello("From Tokio::shutdown()"))?;
    }
    Ok(())
  }
}


#[cfg(feature = "rocket")]
#[derive(Default)]
pub struct MyRocketService {
  pub fail: FailMode,
  pub visited: Arc<Mutex<Visited>>
}

#[cfg(feature = "rocket")]
#[allow(unused)]
impl MyRocketService {
  pub const fn fail_init(mut self) -> Self {
    self.fail.init();
    self
  }
  pub const fn fail_run(mut self) -> Self {
    self.fail.run();
    self
  }
  pub const fn fail_shutdown(mut self) -> Self {
    self.fail.shutdown();
    self
  }
}

#[cfg(feature = "rocket")]
#[qsu::async_trait]
impl RocketServiceHandler for MyRocketService {
  type AppErr = Error;

  async fn init(
    &mut self,
    _ictx: &mut InitCtx
  ) -> Result<Vec<Rocket<Build>>, Self::AppErr> {
    self.visited.lock().init = true;
    if self.fail.init {
      Err(Error::hello("From Rocket::init()"))?;
    }
    Ok(Vec::new())
  }

  async fn run(
    &mut self,
    _rockets: Vec<Rocket<Ignite>>,
    _re: &RunEnv
  ) -> Result<(), Self::AppErr> {
    self.visited.lock().run = true;
    if self.fail.run {
      Err(Error::hello("From Rocket::run()"))?;
    }
    Ok(())
  }

  async fn shutdown(
    &mut self,
    _tctx: &mut TermCtx
  ) -> Result<(), Self::AppErr> {
    self.visited.lock().shutdown = true;
    if self.fail.shutdown {
      Err(Error::hello("From Rocket::shutdown()"))?;
    }
    Ok(())
  }
}

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