#![cfg_attr(docsrs, feature(doc_cfg))]
#![feature(type_alias_impl_trait)]

use std::{fmt::Debug, ops::Deref, sync::Arc};

use anyhow::Result;
use kvid::KvId;
pub use site_log_derive::linkme;
use tosql::ToSqlTrait;
pub mod ctx;
pub use tokio::spawn;

pub type JoinHandle = tokio::task::JoinHandle<Result<()>>;
pub type Task<T> = fn(Ctx<T>) -> JoinHandle;
pub type HookLi<T> = [Task<T>];
pub type HookSlice<T> = &'static linkme::DistributedSlice<HookLi<T>>;

pub static SITE_LOG_ID: KvId = KvId::new("siteLog");

#[derive(Debug)]
pub struct Ctx<T: Debug> {
  pub ctx: Arc<ctx::Ctx>,
  pub inner: Arc<T>,
}

impl<T: Debug> Deref for Ctx<T> {
  type Target = T;
  fn deref(&self) -> &Self::Target {
    &self.inner
  }
}

pub trait SiteLog: Debug + ToSqlTrait + Sized
where
  Self: 'static,
{
  const TYPE_ID: u64;
  const HOOK: HookSlice<Self>;

  fn site_log(self, ctx: Arc<ctx::Ctx>) -> impl Future<Output = Result<()>>
  where
    Self: Sized + Send,
  {
    tokio::spawn({
      let dump = self.dump();
      let ctx = ctx.clone();
      async move {
        use fred::types::streams::XID;
        use xkv::fred::interfaces::StreamsInterface;
        xerr::log!(
          xkv::R
            .xadd::<(), _, _, _, _>(
              &ctx.site_key[..],
              false,
              None,
              XID::Auto,
              (&ctx.ipbin[..], xbin::concat!(ctx.bin, dump)),
            )
            .await
        );
      }
    });
    async move {
      let me = Arc::new(self);
      for i in Self::HOOK.iter().map(|f| {
        f(Ctx {
          ctx: ctx.clone(),
          inner: me.clone(),
        })
      }) {
        xerr::log!(i.await);
      }
      Ok(())
    }
  }
}
