#define CATCH_CONFIG_MAIN

#include "catch2/catch.hpp"

#include "Arduino.h"
#include "catch_utils.hpp"
#include "painlessmesh/validation.hpp"

using namespace painlessmesh;
using namespace painlessmesh::validation;

SCENARIO("Message validation works correctly") {
    GIVEN("A message validator with default configuration") {
        MessageValidator validator;
        
        WHEN("Validating a valid message") {
            JsonDocument doc;
            doc["type"] = 9; // SINGLE message
            doc["from"] = 12345;
            doc["dest"] = 67890;
            doc["msg"] = "Hello World";
            
            JsonObject obj = doc.as<JsonObject>();
            ValidationResult result = validator.validate_message(obj, 100);
            
            THEN("Validation should pass") {
                REQUIRE(result == ValidationResult::VALID);
            }
        }
        
        WHEN("Validating a message without required type field") {
            JsonDocument doc;
            doc["from"] = 12345;
            doc["msg"] = "Hello World";
            
            JsonObject obj = doc.as<JsonObject>();
            ValidationResult result = validator.validate_message(obj, 100);
            
            THEN("Validation should fail with missing field error") {
                REQUIRE(result == ValidationResult::MISSING_REQUIRED_FIELD);
            }
        }
        
        WHEN("Validating a message that is too large") {
            JsonDocument doc;
            doc["type"] = 9;
            doc["from"] = 12345;
            
            JsonObject obj = doc.as<JsonObject>();
            ValidationResult result = validator.validate_message(obj, 10000); // Exceeds default 8KB limit
            
            THEN("Validation should fail with message too large error") {
                REQUIRE(result == ValidationResult::MESSAGE_TOO_LARGE);
            }
        }
        
        WHEN("Validating a message with invalid node ID") {
            JsonDocument doc;
            doc["type"] = 9;
            doc["from"] = 0; // Invalid node ID (0 is reserved for broadcast destination)
            doc["dest"] = 12345;
            
            JsonObject obj = doc.as<JsonObject>();
            ValidationResult result = validator.validate_message(obj, 100);
            
            THEN("Validation should fail with invalid node ID error") {
                REQUIRE(result == ValidationResult::INVALID_NODE_ID);
            }
        }
    }
}

SCENARIO("Rate limiting works correctly") {
    GIVEN("A rate limiter with 3 messages per second limit") {
        RateLimiter limiter(3, 1000);
        uint32_t node_id = 12345;
        
        WHEN("Sending messages within the limit") {
            bool result1 = limiter.allow_message(node_id);
            bool result2 = limiter.allow_message(node_id);
            bool result3 = limiter.allow_message(node_id);
            
            THEN("All messages should be allowed") {
                REQUIRE(result1 == true);
                REQUIRE(result2 == true);  
                REQUIRE(result3 == true);
            }
        }
        
        WHEN("Sending messages beyond the limit") {
            // Send 3 messages (at limit)
            limiter.allow_message(node_id);
            limiter.allow_message(node_id);
            limiter.allow_message(node_id);
            
            // Try to send 4th message
            bool result4 = limiter.allow_message(node_id);
            
            THEN("The excess message should be rejected") {
                REQUIRE(result4 == false);
            }
        }
        
        WHEN("Clearing node history") {
            // Fill up the limit
            limiter.allow_message(node_id);
            limiter.allow_message(node_id);
            limiter.allow_message(node_id);
            
            // Clear history and try again
            limiter.clear_node_history(node_id);
            bool result = limiter.allow_message(node_id);
            
            THEN("Messages should be allowed again") {
                REQUIRE(result == true);
            }
        }
    }
}

SCENARIO("Secure random number generation works") {
    GIVEN("The SecureRandom utility") {
        WHEN("Generating random numbers") {
            uint32_t random1 = SecureRandom::generate();
            uint32_t random2 = SecureRandom::generate();
            
            THEN("Generated numbers should be different") {
                REQUIRE(random1 != random2);
            }
        }
        
        WHEN("Generating random bytes") {
            uint8_t buffer1[16] = {0};
            uint8_t buffer2[16] = {0};
            
            SecureRandom::generate_bytes(buffer1, 16);
            SecureRandom::generate_bytes(buffer2, 16);
            
            THEN("Generated byte arrays should be different") {
                bool are_different = false;
                for (int i = 0; i < 16; i++) {
                    if (buffer1[i] != buffer2[i]) {
                        are_different = true;
                        break;
                    }
                }
                REQUIRE(are_different == true);
            }
        }
    }
}

SCENARIO("Error messages are helpful") {
    GIVEN("A message validator") {
        MessageValidator validator;
        
        WHEN("Getting error messages for different validation results") {
            const char* valid_msg = validator.get_error_message(ValidationResult::VALID);
            const char* invalid_json_msg = validator.get_error_message(ValidationResult::INVALID_JSON);
            const char* missing_field_msg = validator.get_error_message(ValidationResult::MISSING_REQUIRED_FIELD);
            
            THEN("Error messages should be descriptive") {
                REQUIRE(strlen(valid_msg) > 0);
                REQUIRE(strlen(invalid_json_msg) > 0);
                REQUIRE(strlen(missing_field_msg) > 0);
                REQUIRE(strcmp(valid_msg, "Valid") == 0);
                REQUIRE(strcmp(invalid_json_msg, "Invalid JSON format") == 0);
                REQUIRE(strcmp(missing_field_msg, "Missing required field") == 0);
            }
        }
    }
}