// This code is automatically generated by Senax and is always overwritten.

use crate::connection::DbConn;
use ::async_trait::async_trait;
use ::fxhash::FxHashMap;
use ::log::error;
use ::once_cell::sync::OnceCell;
use ::senax_common::ShardId;
use ::senax_encoder::{Decode, Encode, Pack, Unpack};
use ::std::sync::Arc;
use ::tokio::sync::RwLock;

pub const USE_FAST_CACHE: bool = @{ config.use_fast_cache() }@;
pub const USE_STORAGE_CACHE: bool = @{ config.use_storage_cache }@;
pub static CACHE_UPDATE_LOCK: RwLock<()> = RwLock::const_new(());
@{-"\n"}@
@%- for (name, defs) in groups %@
pub mod @{ name|snake|ident }@;
@%- endfor %@

#[derive(serde::Serialize, serde::Deserialize, Hash, PartialEq, Eq, PartialOrd, Clone, Copy, Debug, strum::IntoStaticStr)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
pub enum NotifyOp {
    insert,
    update,
    upsert,
    delete,
    delete_all,
    invalidate,
    invalidate_all,
}

#[derive(serde::Serialize, serde::Deserialize, Hash, PartialEq, Eq, Clone, Copy, Debug, strum::EnumString, strum::IntoStaticStr)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
pub enum TableName {
    @%- for table_name in table_names %@
    @{ table_name|ident }@,
    @%- endfor %@
}

pub(crate) struct CacheActor;

#[derive(Decode, Encode, Clone, Debug)]
#[cfg_attr(all(debug_assertions, not(feature = "production_mode")), senax(disable_encode))]
pub struct CacheMsg(pub Vec<CacheOp>, pub FxHashMap<ShardId, u64>);

#[allow(clippy::large_enum_variant)]
#[derive(Decode, Encode, Pack, Unpack, Clone, Debug)]
#[cfg_attr(all(debug_assertions, not(feature = "production_mode")), senax(disable_pack, disable_encode))]
pub enum CacheOp {
@%- for (name, defs) in groups %@
    @{ name|to_pascal_name }@(@{ name|snake|ident }@::CacheOp),
@%- endfor %@
    _AllClear,
}

impl CacheMsg {
    pub(crate) async fn handle_cache_msg(self) {
        let _lock = CACHE_UPDATE_LOCK.write().await;
        let _sync_map = Arc::new(self.1);
@%- for name in unified_joinable %@
        if let Some(g) = @{ name|upper }@_HANDLER.get() {
            g.handle_cache_msg(self.0.clone(), Arc::clone(&_sync_map)).await;
        }
@%- endfor %@
        DbConn::_publish_update_notice().await;
    }

    pub async fn do_send(self) {
        if !crate::is_test_mode() {
            CacheActor::handle(self);
        } else {
            self.handle_cache_msg().await;
        }
    }

    pub async fn do_send_to_internal(self) {
        self.handle_cache_msg().await;
    }
}

#[rustfmt::skip]
impl CacheActor {
    pub fn handle(msg: CacheMsg) {
        tokio::spawn(
            async move {
                let _guard = crate::get_shutdown_guard();
                if let Some(linker) = crate::LINKER_SENDER.get() {
                    if let Err(e) = linker.send(&msg) {
                        error!(target: "db_@{ db|snake }@::models", "{}", e);
                    }
                }
                msg.handle_cache_msg().await;
            }
        );
    }
}

pub async fn _clear_cache(_sync_map: &FxHashMap<ShardId, u64>, _clear_test: bool) {
@%- if !config.force_disable_cache %@
    for (shard_id, sync) in _sync_map.iter() {
        if *sync == 0 && !_clear_test {
            let shard_id = *shard_id;
            tokio::spawn(async move {
                tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
                @%- for name in unified_joinable %@
                if let Some(g) = @{ name|upper }@_HANDLER.get() {
                    g.clear_cache(shard_id, 0, _clear_test).await;
                }
                @%- endfor %@
            });
        }
        @%- for name in unified_joinable %@
        if let Some(g) = @{ name|upper }@_HANDLER.get() {
            g.clear_cache(*shard_id, *sync, _clear_test).await;
        }
        @%- endfor %@
    }
@%- endif %@
}
@% for name in unified_joinable %@
pub static @{ name|upper }@_HANDLER: OnceCell<Box<dyn CacheHandler + Send + Sync>> = OnceCell::new();
@%- endfor %@

#[async_trait]
pub trait CacheHandler {
    async fn handle_cache_msg(&self, op: Vec<CacheOp>, sync_map: Arc<FxHashMap<ShardId, u64>>);
    async fn clear_cache(&self, shard_id: ShardId, sync: u64, clear_test: bool);
}
@{-"\n"}@