use crate::plugins::auth_enforcement::auth_result::{
    map_auth_result_with_message, take_most_blocking, AuthResult,
};

use super::configuration::HMACRuleConfiguration;
use super::header::HMACAuthorizationHeader;

pub trait HMACChecker {
    fn check_hmac_auth(
        &self,
        configuration: &HMACRuleConfiguration,
        auth: HMACAuthorizationHeader,
    ) -> AuthResult;
}

pub struct RealHMACChecker;

impl HMACChecker for RealHMACChecker {
    fn check_hmac_auth(
        &self,
        configuration: &HMACRuleConfiguration,
        auth: HMACAuthorizationHeader,
    ) -> AuthResult {
        let mut response = map_auth_result_with_message(configuration.hmac_use, "HMAC in use");

        if auth.recipient != "graphql-router" {
            response = take_most_blocking(
                response,
                map_auth_result_with_message(
                    configuration.recipient_mismatch,
                    "Bad HMAC Recipient",
                ),
            );
        }

        response
    }
}

#[cfg(test)]
pub mod mock {
    use super::*;

    pub struct MockHMACChecker {
        result: AuthResult,
    }

    impl MockHMACChecker {
        pub fn new(result: AuthResult) -> MockHMACChecker {
            MockHMACChecker { result }
        }
    }

    impl HMACChecker for MockHMACChecker {
        fn check_hmac_auth(
            &self,
            _configuration: &HMACRuleConfiguration,
            _auth: HMACAuthorizationHeader,
        ) -> AuthResult {
            self.result
        }
    }
}

#[cfg(test)]
mod test {
    use crate::plugins::auth_enforcement::auth_result::AuthResultConfiguration;

    use super::*;

    #[test]
    fn test_check_hmac_auth_hmac_in_use_ok() {
        let mut config = HMACRuleConfiguration::all_warning();
        config.hmac_use = AuthResultConfiguration::Ok;

        let auth = HMACAuthorizationHeader {
            sender: "sender",
            recipient: "graphql-router",
            hmac: "5745fd9c0640945e8b744def34ebafff9e9f6846",
        };

        let result = RealHMACChecker {}.check_hmac_auth(&config, auth);
        assert_eq!(result, AuthResult::Ok);
    }

    #[test]
    fn test_check_hmac_auth_hmac_in_use_warning() {
        let mut config = HMACRuleConfiguration::all_block();
        config.hmac_use = AuthResultConfiguration::Warning;

        let auth = HMACAuthorizationHeader {
            sender: "sender",
            recipient: "graphql-router",
            hmac: "5745fd9c0640945e8b744def34ebafff9e9f6846",
        };

        let result = RealHMACChecker {}.check_hmac_auth(&config, auth);
        assert_eq!(
            result,
            AuthResult::Warning {
                message: "HMAC in use"
            }
        );
    }

    #[test]
    fn test_check_hmac_auth_hmac_in_use_block() {
        let mut config = HMACRuleConfiguration::all_ok();
        config.hmac_use = AuthResultConfiguration::Block;

        let auth = HMACAuthorizationHeader {
            sender: "sender",
            recipient: "graphql-router",
            hmac: "5745fd9c0640945e8b744def34ebafff9e9f6846",
        };

        let result = RealHMACChecker {}.check_hmac_auth(&config, auth);
        assert_eq!(
            result,
            AuthResult::Block {
                message: "HMAC in use"
            }
        );
    }

    #[test]
    fn test_check_hmac_auth_recipient_mismatch_ok() {
        let mut config = HMACRuleConfiguration::all_warning();
        config.hmac_use = AuthResultConfiguration::Ok;
        config.recipient_mismatch = AuthResultConfiguration::Ok;

        let auth = HMACAuthorizationHeader {
            sender: "sender",
            recipient: "not-graphql-router",
            hmac: "5745fd9c0640945e8b744def34ebafff9e9f6846",
        };

        let result = RealHMACChecker {}.check_hmac_auth(&config, auth);
        assert_eq!(result, AuthResult::Ok);
    }

    #[test]
    fn test_check_hmac_auth_recipient_mismatch_warning() {
        let mut config = HMACRuleConfiguration::all_block();
        config.hmac_use = AuthResultConfiguration::Ok;
        config.recipient_mismatch = AuthResultConfiguration::Warning;

        let auth = HMACAuthorizationHeader {
            sender: "sender",
            recipient: "not-graphql-router",
            hmac: "5745fd9c0640945e8b744def34ebafff9e9f6846",
        };

        let result = RealHMACChecker {}.check_hmac_auth(&config, auth);
        assert_eq!(
            result,
            AuthResult::Warning {
                message: "Bad HMAC Recipient"
            }
        );
    }

    #[test]
    fn test_check_hmac_auth_recipient_mismatch_block() {
        let mut config = HMACRuleConfiguration::all_ok();
        config.hmac_use = AuthResultConfiguration::Ok;
        config.recipient_mismatch = AuthResultConfiguration::Block;

        let auth = HMACAuthorizationHeader {
            sender: "sender",
            recipient: "not-graphql-router",
            hmac: "5745fd9c0640945e8b744def34ebafff9e9f6846",
        };

        let result = RealHMACChecker {}.check_hmac_auth(&config, auth);
        assert_eq!(
            result,
            AuthResult::Block {
                message: "Bad HMAC Recipient"
            }
        );
    }
}
