use db::cache::AppCache;
use db::database::AppDatabase;
use models::rooms::room_message::{Column as RmCol, Entity as RoomMessage};
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QuerySelect};
use uuid::Uuid;

use crate::error::RoomError;

pub async fn next_room_message_seq_internal(
    room_id: Uuid,
    db: &AppDatabase,
    cache: &AppCache,
) -> Result<i64, RoomError> {
    let seq_key = format!("room:seq:{}", room_id);
    let mut conn = cache.conn().await.map_err(|e| {
        RoomError::Internal(format!("failed to get redis connection for seq: {}", e))
    })?;

    let seq: i64 = redis::cmd("INCR")
        .arg(&seq_key)
        .query_async(&mut conn)
        .await
        .map_err(|e| RoomError::Internal(format!("seq INCR: {}", e)))?;

    // DB reconciliation: only check every 1000 messages
    if seq % 1000 == 0 {
        let db_seq: Option<Option<Option<i64>>> = RoomMessage::find()
            .filter(RmCol::Room.eq(room_id))
            .select_only()
            .column_as(RmCol::Seq.max(), "max_seq")
            .into_tuple::<Option<Option<i64>>>()
            .one(db)
            .await?
            .map(|r| r);
        let db_seq = db_seq.flatten().flatten().unwrap_or(0);

        if db_seq >= seq {
            let _: String = redis::cmd("SET")
                .arg(&seq_key)
                .arg(db_seq + 1)
                .query_async(&mut conn)
                .await
                .map_err(|e| RoomError::Internal(format!("seq SET: {}", e)))?;
            return Ok(db_seq + 1);
        }
    }

    Ok(seq)
}
