#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <cstdlib>
#include <cstring>
#include <map>
#include <string>
#include <algorithm>

// We need to extract the ArgumentParser class to test it
// For now, let's create a simplified version for testing
class TestArgumentParser {
private:
    std::map<std::string, std::string> args_;
    std::map<std::string, std::string> env_vars_;
    
public:
    TestArgumentParser() {
        // Load environment variables with GPIO_ prefix
        extern char **environ;
        for (char **env = environ; *env != nullptr; env++) {
            std::string env_str(*env);
            if (env_str.substr(0, 5) == "GPIO_") {
                size_t eq_pos = env_str.find('=');
                if (eq_pos != std::string::npos) {
                    std::string key = env_str.substr(5, eq_pos - 5); // Remove GPIO_ prefix
                    std::string value = env_str.substr(eq_pos + 1);
                    // Convert to lowercase with dashes
                    std::transform(key.begin(), key.end(), key.begin(), ::tolower);
                    std::replace(key.begin(), key.end(), '_', '-');
                    env_vars_[key] = value;
                }
            }
        }
    }
    
    void parse(int argc, char* argv[]) {
        for (int i = 1; i < argc; i++) {
            std::string arg(argv[i]);
            if (arg.substr(0, 2) == "--") {
                std::string key = arg.substr(2);
                if (i + 1 < argc && std::string(argv[i + 1]).substr(0, 2) != "--") {
                    args_[key] = argv[++i];
                } else {
                    args_[key] = "true";
                }
            }
        }
    }
    
    std::string get(const std::string& key, const std::string& default_value = "") const {
        if (args_.find(key) != args_.end()) {
            return args_.at(key);
        }
        if (env_vars_.find(key) != env_vars_.end()) {
            return env_vars_.at(key);
        }
        return default_value;
    }
    
    int get_int(const std::string& key, int default_value = 0) const {
        std::string value = get(key);
        if (value.empty()) return default_value;
        try {
            return std::stoi(value);
        } catch (...) {
            return default_value;
        }
    }
    
    double get_double(const std::string& key, double default_value = 0.0) const {
        std::string value = get(key);
        if (value.empty()) return default_value;
        try {
            return std::stod(value);
        } catch (...) {
            return default_value;
        }
    }
    
    bool get_bool(const std::string& key, bool default_value = false) const {
        std::string value = get(key);
        if (value.empty()) return default_value;
        return value == "true" || value == "1" || value == "True" || value == "TRUE";
    }
    
    // Test helper to set environment variables
    void set_env_var(const std::string& key, const std::string& value) {
        env_vars_[key] = value;
    }
    
    void clear_env_vars() {
        env_vars_.clear();
    }
};

TEST_CASE("Argument Parser - Command Line Arguments", "[parser][args]") {
    TestArgumentParser parser;
    
    SECTION("Parse simple arguments") {
        const char* argv[] = {"systemd-gpio", "--pin", "23", "--pull-up", "true"};
        int argc = sizeof(argv) / sizeof(argv[0]);
        
        parser.parse(argc, const_cast<char**>(argv));
        
        REQUIRE(parser.get("pin") == "23");
        REQUIRE(parser.get("pull-up") == "true");
        REQUIRE(parser.get_int("pin") == 23);
        REQUIRE(parser.get_bool("pull-up") == true);
    }
    
    SECTION("Parse boolean flags") {
        const char* argv[] = {"systemd-gpio", "--help", "--version"};
        int argc = sizeof(argv) / sizeof(argv[0]);
        
        parser.parse(argc, const_cast<char**>(argv));
        
        REQUIRE(parser.get("help") == "true");
        REQUIRE(parser.get("version") == "true");
        REQUIRE(parser.get_bool("help") == true);
        REQUIRE(parser.get_bool("version") == true);
    }
    
    SECTION("Parse numeric arguments") {
        const char* argv[] = {"systemd-gpio", "--pin", "18", "--bounce-time", "0.05", "--hold-time", "2.5"};
        int argc = sizeof(argv) / sizeof(argv[0]);
        
        parser.parse(argc, const_cast<char**>(argv));
        
        REQUIRE(parser.get_int("pin") == 18);
        REQUIRE(parser.get_double("bounce-time") == Catch::Approx(0.05));
        REQUIRE(parser.get_double("hold-time") == Catch::Approx(2.5));
    }
    
    SECTION("Handle missing arguments with defaults") {
        const char* argv[] = {"systemd-gpio"};
        int argc = sizeof(argv) / sizeof(argv[0]);
        
        parser.parse(argc, const_cast<char**>(argv));
        
        REQUIRE(parser.get("pin", "default") == "default");
        REQUIRE(parser.get_int("pin", 42) == 42);
        REQUIRE(parser.get_bool("pull-up", true) == true);
        REQUIRE(parser.get_double("bounce-time", 0.1) == Catch::Approx(0.1));
    }
}

TEST_CASE("Argument Parser - Environment Variables", "[parser][env]") {
    TestArgumentParser parser;
    
    SECTION("Parse environment variables") {
        parser.clear_env_vars();
        parser.set_env_var("pin", "24");
        parser.set_env_var("pull-up", "0");
        parser.set_env_var("active-state", "1");
        parser.set_env_var("bounce-time", "0.02");
        
        REQUIRE(parser.get_int("pin") == 24);
        REQUIRE(parser.get_bool("pull-up") == false);
        REQUIRE(parser.get_bool("active-state") == true);
        REQUIRE(parser.get_double("bounce-time") == Catch::Approx(0.02));
    }
    
    SECTION("Command line overrides environment variables") {
        parser.clear_env_vars();
        parser.set_env_var("pin", "18");
        parser.set_env_var("pull-up", "0");
        
        const char* argv[] = {"systemd-gpio", "--pin", "23", "--pull-up", "true"};
        int argc = sizeof(argv) / sizeof(argv[0]);
        
        parser.parse(argc, const_cast<char**>(argv));
        
        // Command line should override environment
        REQUIRE(parser.get_int("pin") == 23);
        REQUIRE(parser.get_bool("pull-up") == true);
    }
}

TEST_CASE("Argument Parser - Type Conversions", "[parser][types]") {
    TestArgumentParser parser;
    
    SECTION("Boolean conversions") {
        parser.clear_env_vars();
        parser.set_env_var("test1", "true");
        parser.set_env_var("test2", "1");
        parser.set_env_var("test3", "True");
        parser.set_env_var("test4", "TRUE");
        parser.set_env_var("test5", "false");
        parser.set_env_var("test6", "0");
        parser.set_env_var("test7", "");
        
        REQUIRE(parser.get_bool("test1") == true);
        REQUIRE(parser.get_bool("test2") == true);
        REQUIRE(parser.get_bool("test3") == true);
        REQUIRE(parser.get_bool("test4") == true);
        REQUIRE(parser.get_bool("test5") == false);
        REQUIRE(parser.get_bool("test6") == false);
        REQUIRE(parser.get_bool("test7") == false);
    }
    
    SECTION("Integer conversions") {
        parser.clear_env_vars();
        parser.set_env_var("valid", "42");
        parser.set_env_var("negative", "-10");
        parser.set_env_var("invalid", "abc");
        parser.set_env_var("empty", "");
        
        REQUIRE(parser.get_int("valid") == 42);
        REQUIRE(parser.get_int("negative") == -10);
        REQUIRE(parser.get_int("invalid", 999) == 999); // Default on error
        REQUIRE(parser.get_int("empty", 123) == 123);   // Default on empty
    }
    
    SECTION("Double conversions") {
        parser.clear_env_vars();
        parser.set_env_var("valid", "3.14");
        parser.set_env_var("negative", "-2.5");
        parser.set_env_var("integer", "42");
        parser.set_env_var("invalid", "not_a_number");
        
        REQUIRE(parser.get_double("valid") == Catch::Approx(3.14));
        REQUIRE(parser.get_double("negative") == Catch::Approx(-2.5));
        REQUIRE(parser.get_double("integer") == Catch::Approx(42.0));
        REQUIRE(parser.get_double("invalid", 1.0) == Catch::Approx(1.0)); // Default on error
    }
}

TEST_CASE("Argument Parser - All GPIO Options", "[parser][comprehensive]") {
    TestArgumentParser parser;
    
    SECTION("All command-line options") {
        const char* argv[] = {
            "systemd-gpio", 
            "--pin", "23",
            "--pull-up", "false",
            "--active-state", "true", 
            "--bounce-time", "0.05",
            "--hold-time", "2.0",
            "--hold-repeat", "true",
            "--when-pressed", "echo pressed",
            "--when-released", "echo released", 
            "--when-held", "echo held"
        };
        int argc = sizeof(argv) / sizeof(argv[0]);
        
        parser.parse(argc, const_cast<char**>(argv));
        
        REQUIRE(parser.get_int("pin") == 23);
        REQUIRE(parser.get_bool("pull-up") == false);
        REQUIRE(parser.get_bool("active-state") == true);
        REQUIRE(parser.get_double("bounce-time") == Catch::Approx(0.05));
        REQUIRE(parser.get_double("hold-time") == Catch::Approx(2.0));
        REQUIRE(parser.get_bool("hold-repeat") == true);
        REQUIRE(parser.get("when-pressed") == "echo pressed");
        REQUIRE(parser.get("when-released") == "echo released");
        REQUIRE(parser.get("when-held") == "echo held");
    }
    
    SECTION("All environment variables") {
        parser.clear_env_vars();
        parser.set_env_var("pin", "18");
        parser.set_env_var("pull-up", "1");
        parser.set_env_var("active-state", "0");
        parser.set_env_var("bounce-time", "0.02");
        parser.set_env_var("hold-time", "1.5");
        parser.set_env_var("hold-repeat", "True");
        parser.set_env_var("when-pressed", "systemctl start service1");
        parser.set_env_var("when-released", "systemctl stop service1");
        parser.set_env_var("when-held", "systemctl restart service1");
        
        REQUIRE(parser.get_int("pin") == 18);
        REQUIRE(parser.get_bool("pull-up") == true);
        REQUIRE(parser.get_bool("active-state") == false);
        REQUIRE(parser.get_double("bounce-time") == Catch::Approx(0.02));
        REQUIRE(parser.get_double("hold-time") == Catch::Approx(1.5));
        REQUIRE(parser.get_bool("hold-repeat") == true);
        REQUIRE(parser.get("when-pressed") == "systemctl start service1");
        REQUIRE(parser.get("when-released") == "systemctl stop service1");
        REQUIRE(parser.get("when-held") == "systemctl restart service1");
    }
    
    SECTION("Command strings with spaces and special characters") {
        const char* argv[] = {
            "systemd-gpio",
            "--pin", "24",
            "--when-pressed", "echo 'Button pressed at $(date)'",
            "--when-released", "/usr/bin/logger -t gpio 'Button released'",
            "--when-held", "wall 'Emergency shutdown in 10 seconds' && sleep 10 && shutdown -h now"
        };
        int argc = sizeof(argv) / sizeof(argv[0]);
        
        parser.parse(argc, const_cast<char**>(argv));
        
        REQUIRE(parser.get("when-pressed") == "echo 'Button pressed at $(date)'");
        REQUIRE(parser.get("when-released") == "/usr/bin/logger -t gpio 'Button released'");
        REQUIRE(parser.get("when-held") == "wall 'Emergency shutdown in 10 seconds' && sleep 10 && shutdown -h now");
    }
    
    SECTION("Mixed command-line and environment variables") {
        parser.clear_env_vars();
        // Set some environment variables
        parser.set_env_var("pin", "25");
        parser.set_env_var("bounce-time", "0.1");
        parser.set_env_var("when-pressed", "env_command");
        
        // Override some with command line
        const char* argv[] = {
            "systemd-gpio",
            "--pin", "26",  // Override env
            "--hold-time", "3.0",  // Not in env
            "--when-released", "cli_command"  // Not in env
        };
        int argc = sizeof(argv) / sizeof(argv[0]);
        
        parser.parse(argc, const_cast<char**>(argv));
        
        // Command line should override environment
        REQUIRE(parser.get_int("pin") == 26);
        // Command line only
        REQUIRE(parser.get_double("hold-time") == Catch::Approx(3.0));
        REQUIRE(parser.get("when-released") == "cli_command");
        // Environment only
        REQUIRE(parser.get_double("bounce-time") == Catch::Approx(0.1));
        REQUIRE(parser.get("when-pressed") == "env_command");
    }
}

TEST_CASE("Argument Parser - Edge Cases", "[parser][edge-cases]") {
    TestArgumentParser parser;
    
    SECTION("Empty command strings") {
        const char* argv[] = {
            "systemd-gpio",
            "--pin", "23",
            "--when-pressed", "",
            "--when-released", "",
            "--when-held", ""
        };
        int argc = sizeof(argv) / sizeof(argv[0]);
        
        parser.parse(argc, const_cast<char**>(argv));
        
        REQUIRE(parser.get("when-pressed") == "");
        REQUIRE(parser.get("when-released") == "");
        REQUIRE(parser.get("when-held") == "");
    }
    
    SECTION("Boolean variations") {
        parser.clear_env_vars();
        parser.set_env_var("test-true1", "true");
        parser.set_env_var("test-true2", "TRUE");
        parser.set_env_var("test-true3", "True");
        parser.set_env_var("test-true4", "1");
        parser.set_env_var("test-false1", "false");
        parser.set_env_var("test-false2", "FALSE");
        parser.set_env_var("test-false3", "False");
        parser.set_env_var("test-false4", "0");
        parser.set_env_var("test-false5", "anything_else");
        parser.set_env_var("test-false6", "");
        
        REQUIRE(parser.get_bool("test-true1") == true);
        REQUIRE(parser.get_bool("test-true2") == true);
        REQUIRE(parser.get_bool("test-true3") == true);
        REQUIRE(parser.get_bool("test-true4") == true);
        REQUIRE(parser.get_bool("test-false1") == false);
        REQUIRE(parser.get_bool("test-false2") == false);
        REQUIRE(parser.get_bool("test-false3") == false);
        REQUIRE(parser.get_bool("test-false4") == false);
        REQUIRE(parser.get_bool("test-false5") == false);
        REQUIRE(parser.get_bool("test-false6") == false);
    }
    
    SECTION("Numeric edge cases") {
        parser.clear_env_vars();
        parser.set_env_var("zero", "0");
        parser.set_env_var("negative", "-5");
        parser.set_env_var("float-zero", "0.0");
        parser.set_env_var("scientific", "1e-3");
        parser.set_env_var("large", "999999");
        
        REQUIRE(parser.get_int("zero") == 0);
        REQUIRE(parser.get_int("negative") == -5);
        REQUIRE(parser.get_double("float-zero") == Catch::Approx(0.0));
        REQUIRE(parser.get_double("scientific") == Catch::Approx(0.001));
        REQUIRE(parser.get_int("large") == 999999);
    }
    
    SECTION("Default values for all option types") {
        // Parser with no arguments or environment variables
        const char* argv[] = {"systemd-gpio"};
        int argc = sizeof(argv) / sizeof(argv[0]);
        parser.clear_env_vars();
        parser.parse(argc, const_cast<char**>(argv));
        
        // Test defaults for all supported options
        REQUIRE(parser.get_int("pin", -1) == -1);
        REQUIRE(parser.get_bool("pull-up", true) == true);
        REQUIRE(parser.get_bool("active-state", false) == false);
        REQUIRE(parser.get_double("bounce-time", 0.0) == Catch::Approx(0.0));
        REQUIRE(parser.get_double("hold-time", 1.0) == Catch::Approx(1.0));
        REQUIRE(parser.get_bool("hold-repeat", false) == false);
        REQUIRE(parser.get("when-pressed", "") == "");
        REQUIRE(parser.get("when-released", "") == "");
        REQUIRE(parser.get("when-held", "") == "");
    }
}

TEST_CASE("Argument Parser - Negative Tests", "[parser][negative]") {
    TestArgumentParser parser;
    
    SECTION("Invalid numeric conversions") {
        parser.clear_env_vars();
        parser.set_env_var("invalid-int", "not_a_number");
        parser.set_env_var("invalid-double", "definitely_not_a_number");
        parser.set_env_var("overflow-int", "999999999999999999999");
        parser.set_env_var("empty-numeric", "");
        
        // Should return default values on conversion errors
        REQUIRE(parser.get_int("invalid-int", 42) == 42);
        REQUIRE(parser.get_double("invalid-double", 3.14) == Catch::Approx(3.14));
        REQUIRE(parser.get_int("overflow-int", 999) == 999); // Overflow should use default
        REQUIRE(parser.get_int("empty-numeric", 123) == 123);
    }
    
    SECTION("Invalid pin numbers") {
        // Test various invalid pin scenarios
        parser.clear_env_vars();
        parser.set_env_var("negative-pin", "-5");
        parser.set_env_var("zero-pin", "0");  // Pin 0 might be valid on some systems
        parser.set_env_var("large-pin", "999");  // Very high pin numbers
        parser.set_env_var("text-pin", "GPIO23");
        
        REQUIRE(parser.get_int("negative-pin") == -5);  // Parser allows it, validation should happen elsewhere
        REQUIRE(parser.get_int("zero-pin") == 0);
        REQUIRE(parser.get_int("large-pin") == 999);
        REQUIRE(parser.get_int("text-pin", -1) == -1);  // Should use default on error
    }
    
    SECTION("Invalid time values") {
        parser.clear_env_vars();
        parser.set_env_var("negative-bounce", "-0.5");
        parser.set_env_var("negative-hold", "-2.0");
        parser.set_env_var("zero-hold", "0.0");  // Zero hold time should be valid
        parser.set_env_var("huge-time", "999999.0");
        
        REQUIRE(parser.get_double("negative-bounce") == Catch::Approx(-0.5));  // Parser allows negative
        REQUIRE(parser.get_double("negative-hold") == Catch::Approx(-2.0));
        REQUIRE(parser.get_double("zero-hold") == Catch::Approx(0.0));
        REQUIRE(parser.get_double("huge-time") == Catch::Approx(999999.0));
    }
    
    SECTION("Malformed command line arguments") {
        // Test arguments without values
        const char* argv1[] = {"systemd-gpio", "--pin"};  // Missing value
        int argc1 = sizeof(argv1) / sizeof(argv1[0]);
        
        parser.parse(argc1, const_cast<char**>(argv1));
        // Should treat as boolean flag when no value provided
        REQUIRE(parser.get("pin") == "true");
        
        // Test empty arguments
        const char* argv2[] = {"systemd-gpio", "", "--pin", "23"};
        int argc2 = sizeof(argv2) / sizeof(argv2[0]);
        
        TestArgumentParser parser2;
        parser2.parse(argc2, const_cast<char**>(argv2));
        REQUIRE(parser2.get_int("pin") == 23);  // Should still work despite empty arg
    }
    
    SECTION("Conflicting boolean values") {
        parser.clear_env_vars();
        parser.set_env_var("pull-up", "maybe");  // Invalid boolean
        parser.set_env_var("active-state", "yes");  // Invalid boolean
        parser.set_env_var("hold-repeat", "no");   // Invalid boolean
        
        // All should be false since they're not recognized true values
        REQUIRE(parser.get_bool("pull-up") == false);
        REQUIRE(parser.get_bool("active-state") == false);
        REQUIRE(parser.get_bool("hold-repeat") == false);
    }
    
    SECTION("Very long command strings") {
        std::string very_long_command(10000, 'x');  // 10KB command string
        parser.clear_env_vars();
        parser.set_env_var("when-pressed", very_long_command);
        
        REQUIRE(parser.get("when-pressed") == very_long_command);  // Should handle large strings
    }
    
    SECTION("Special characters in commands") {
        parser.clear_env_vars();
        parser.set_env_var("when-pressed", "echo 'test with \\\"quotes\\\" and $variables'");
        parser.set_env_var("when-released", "command; rm -rf / # dangerous");
        parser.set_env_var("when-held", "echo $((2+2)) && echo `date`");
        
        // Parser should preserve all characters - security is handled at execution
        REQUIRE(parser.get("when-pressed") == "echo 'test with \\\"quotes\\\" and $variables'");
        REQUIRE(parser.get("when-released") == "command; rm -rf / # dangerous");
        REQUIRE(parser.get("when-held") == "echo $((2+2)) && echo `date`");
    }
}
