use schemars::JsonSchema;
use serde::Deserialize;

#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, PartialEq)]
pub enum AuthResultConfiguration {
    Ok,
    Warning,
    Block,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AuthResult {
    Ok,
    Warning { message: &'static str },
    Block { message: &'static str },
}

pub fn map_auth_result_with_message(
    configuration: AuthResultConfiguration,
    message: &'static str,
) -> AuthResult {
    match configuration {
        AuthResultConfiguration::Ok => AuthResult::Ok,
        AuthResultConfiguration::Warning => AuthResult::Warning { message },
        AuthResultConfiguration::Block => AuthResult::Block { message },
    }
}

pub fn take_most_blocking(result1: AuthResult, result2: AuthResult) -> AuthResult {
    match (result1, result2) {
        (AuthResult::Ok, result) => result,
        (result, AuthResult::Ok) => result,
        (result, AuthResult::Warning { message }) | (AuthResult::Warning { message }, result) => {
            tracing::warn!("Secondary warning ignored: {}", message);
            result
        }
        (result @ AuthResult::Block { .. }, AuthResult::Block { message }) => {
            tracing::error!("Secondary error ignored: {}", message);
            result
        }
    }
}

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

    #[test]
    fn test_map_auth_result_with_message_ok() {
        let result = map_auth_result_with_message(AuthResultConfiguration::Ok, "A message.");
        assert_eq!(result, AuthResult::Ok);
    }

    #[test]
    fn test_map_auth_result_with_message_warning() {
        let result = map_auth_result_with_message(AuthResultConfiguration::Warning, "A message.");
        assert_eq!(
            result,
            AuthResult::Warning {
                message: "A message."
            }
        );
    }

    #[test]
    fn test_map_auth_result_with_message_block() {
        let result = map_auth_result_with_message(AuthResultConfiguration::Block, "A message.");
        assert_eq!(
            result,
            AuthResult::Block {
                message: "A message."
            }
        );
    }

    #[test]
    fn take_most_blocking_ok_ok() {
        let result1 = AuthResult::Ok;
        let result2 = AuthResult::Ok;

        let result = take_most_blocking(result1, result2);

        assert_eq!(result, AuthResult::Ok);
    }

    #[test]
    fn take_most_blocking_ok_warning() {
        let result1 = AuthResult::Ok;
        let result2 = AuthResult::Warning {
            message: "Warning 2",
        };

        let result = take_most_blocking(result1, result2);

        assert_eq!(
            result,
            AuthResult::Warning {
                message: "Warning 2"
            }
        );
    }

    #[test]
    fn take_most_blocking_ok_block() {
        let result1 = AuthResult::Ok;
        let result2 = AuthResult::Block { message: "Block 2" };

        let result = take_most_blocking(result1, result2);

        assert_eq!(result, AuthResult::Block { message: "Block 2" });
    }

    #[test]
    fn take_most_blocking_warning_ok() {
        let result1 = AuthResult::Warning {
            message: "Warning 1",
        };
        let result2 = AuthResult::Ok;

        let result = take_most_blocking(result1, result2);

        assert_eq!(
            result,
            AuthResult::Warning {
                message: "Warning 1"
            }
        );
    }

    #[test]
    fn take_most_blocking_warning_warning() {
        let result1 = AuthResult::Warning {
            message: "Warning 1",
        };
        let result2 = AuthResult::Warning {
            message: "Warning 2",
        };

        let result = take_most_blocking(result1, result2);

        assert_eq!(
            result,
            AuthResult::Warning {
                message: "Warning 1"
            }
        );
    }

    #[test]
    fn take_most_blocking_warning_block() {
        let result1 = AuthResult::Warning {
            message: "Warning 1",
        };
        let result2 = AuthResult::Block { message: "Block 2" };

        let result = take_most_blocking(result1, result2);

        assert_eq!(result, AuthResult::Block { message: "Block 2" });
    }

    #[test]
    fn take_most_blocking_block_ok() {
        let result1 = AuthResult::Block { message: "Block 1" };
        let result2 = AuthResult::Ok;

        let result = take_most_blocking(result1, result2);

        assert_eq!(result, AuthResult::Block { message: "Block 1" });
    }

    #[test]
    fn take_most_blocking_block_warning() {
        let result1 = AuthResult::Block { message: "Block 1" };
        let result2 = AuthResult::Warning {
            message: "Warning 2",
        };

        let result = take_most_blocking(result1, result2);

        assert_eq!(result, AuthResult::Block { message: "Block 1" });
    }

    #[test]
    fn take_most_blocking_block_block() {
        let result1 = AuthResult::Block { message: "Block 1" };
        let result2 = AuthResult::Block { message: "Block 2" };

        let result = take_most_blocking(result1, result2);

        assert_eq!(result, AuthResult::Block { message: "Block 1" });
    }
}
