use solana_pubkey::Pubkey;

use crate::RedisKey;

impl RedisKey {
    // ── Kamino-specific prefixes ─────────────────────────────────────

    pub const KAMINO_OBLIGATION_PREFIX: &'static str = "account:kamino:obligation";
    pub const KAMINO_OBLIGATION_BY_PUBKEY_PREFIX: &'static str = "reverse:kamino_obligation_pubkey";
    pub const KAMINO_RESERVE_PREFIX: &'static str = "account:kamino:reserve";

    // ── Kamino-specific keys ─────────────────────────────────────────

    pub fn kamino_obligation(vault_address: &Pubkey, lending_market: &Pubkey) -> Self {
        let p = Self::KAMINO_OBLIGATION_PREFIX;
        Self::from_string(format!("{p}:{vault_address}:{lending_market}"))
    }

    /// Reverse mapping: Kamino Obligation account pubkey → vault authority.
    pub fn kamino_obligation_by_pubkey(obligation_pubkey: &Pubkey) -> Self {
        let p = Self::KAMINO_OBLIGATION_BY_PUBKEY_PREFIX;
        Self::from_string(format!("{p}:{obligation_pubkey}"))
    }

    pub fn kamino_reserve(reserve_pubkey: &Pubkey) -> Self {
        let p = Self::KAMINO_RESERVE_PREFIX;
        Self::from_string(format!("{p}:{reserve_pubkey}"))
    }

    // ── Glob patterns for SCAN ───────────────────────────────────────

    pub fn kamino_obligation_glob() -> String {
        Self::pattern(Self::KAMINO_OBLIGATION_PREFIX)
    }

    pub fn kamino_reserve_glob() -> String {
        Self::pattern(Self::KAMINO_RESERVE_PREFIX)
    }
}

#[cfg(test)]
#[allow(
    clippy::allow_attributes,
    clippy::allow_attributes_without_reason,
    clippy::unwrap_used,
    clippy::expect_used,
    clippy::panic,
    clippy::arithmetic_side_effects,
    reason = "test code"
)]
mod tests {
    use super::*;
    use std::str::FromStr;

    #[test]
    fn kamino_obligation_key_format() {
        let vault = Pubkey::from_str("11111111111111111111111111111111").unwrap();
        let market = Pubkey::new_from_array([2; 32]);
        let key = RedisKey::kamino_obligation(&vault, &market);
        let expected = format!(
            "account:kamino:obligation:{vault}:{market}"
        );
        assert_eq!(key.to_string(), expected);
    }

    #[test]
    fn kamino_obligation_by_pubkey_key_format() {
        let pk = Pubkey::from_str("11111111111111111111111111111111").unwrap();
        let key = RedisKey::kamino_obligation_by_pubkey(&pk);
        assert_eq!(
            key.to_string(),
            "reverse:kamino_obligation_pubkey:11111111111111111111111111111111"
        );
    }

    #[test]
    fn kamino_reserve_key_format() {
        let pk = Pubkey::from_str("11111111111111111111111111111111").unwrap();
        let key = RedisKey::kamino_reserve(&pk);
        assert_eq!(
            key.to_string(),
            "account:kamino:reserve:11111111111111111111111111111111"
        );
    }

    #[test]
    fn kamino_obligation_glob_format() {
        assert_eq!(
            RedisKey::kamino_obligation_glob(),
            "account:kamino:obligation:*"
        );
    }

    #[test]
    fn kamino_reserve_glob_format() {
        assert_eq!(RedisKey::kamino_reserve_glob(), "account:kamino:reserve:*");
    }
}
