#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <functional>
#include <chrono>
#include <thread>
#include <csignal>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <filesystem>
#include <memory>
#include <atomic>
#include <algorithm>

#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>

#include "gpio_interface.hpp"
#include "version.hpp"

// Simple argument parser for our specific needs
class ArgumentParser {
private:
    std::map<std::string, std::string> args_;
    std::map<std::string, std::string> env_vars_;
    
public:
    ArgumentParser() {
        // 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 {
        // Command line args take precedence over environment variables
        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";
    }
};

// Command execution utility
void run_command(const std::string& message, const std::string& command) {
    std::cerr << message << std::endl;
    
    // Parse command into arguments
    std::vector<std::string> args;
    std::istringstream iss(command);
    std::string arg;
    bool in_quotes = false;
    std::string current_arg;
    
    for (char c : command) {
        if (c == '"' || c == '\'') {
            in_quotes = !in_quotes;
        } else if (c == ' ' && !in_quotes) {
            if (!current_arg.empty()) {
                args.push_back(current_arg);
                current_arg.clear();
            }
        } else {
            current_arg += c;
        }
    }
    if (!current_arg.empty()) {
        args.push_back(current_arg);
    }
    
    if (args.empty()) return;
    
    // Convert to char* array for execvp
    std::vector<char*> argv;
    for (auto& arg : args) {
        argv.push_back(const_cast<char*>(arg.c_str()));
    }
    argv.push_back(nullptr);
    
    pid_t pid = fork();
    if (pid == 0) {
        // Child process
        execvp(argv[0], argv.data());
        exit(1);
    } else if (pid > 0) {
        // Parent process - don't wait (equivalent to P_NOWAIT)
        // The child will be reaped by init
    }
}

// Global signal handler
std::atomic<bool> keep_running(true);

void signal_handler(int) {
    keep_running = false;
}

void print_help() {
    std::cout << "Usage: systemd-gpio [OPTIONS]\n\n";
    std::cout << "Run commands when a button on a GPIO pin is pressed, released, or held.\n\n";
    std::cout << "Options:\n";
    std::cout << "  --pin INTEGER           GPIO pin number (required)\n";
    std::cout << "  --chip INTEGER          GPIO chip number (default: 0, /dev/gpiochip0)\n";
    std::cout << "  --pull-up BOOLEAN       Enable pull-up resistor (default: true)\n";
    std::cout << "  --active-state BOOLEAN  Active state logic (default: false)\n";
    std::cout << "  --bounce-time FLOAT     Debounce time in seconds\n";
    std::cout << "  --hold-time FLOAT       Hold time in seconds (default: 1.0)\n";
    std::cout << "  --hold-repeat BOOLEAN   Allow repeated hold events (default: false)\n";
    std::cout << "  --when-held STRING      Command to run when button is held\n";
    std::cout << "  --when-pressed STRING   Command to run when button is pressed\n";
    std::cout << "  --when-released STRING  Command to run when button is released\n";
    std::cout << "  --help                  Show this help message\n";
    std::cout << "  --version               Show version information\n\n";
    std::cout << "Environment variables with GPIO_ prefix can also be used.\n";
}

void print_version() {
    std::cout << SYSTEMD_GPIO_PROJECT_NAME << " version " << SYSTEMD_GPIO_VERSION << "\n";
    std::cout << SYSTEMD_GPIO_DESCRIPTION << "\n";
}

int main(int argc, char* argv[]) {
    ArgumentParser parser;
    parser.parse(argc, argv);
    
    // Check for help or version
    if (parser.get("help") == "true") {
        print_help();
        return 0;
    }
    
    if (parser.get("version") == "true") {
        print_version();
        return 0;
    }
    
    // Validate required arguments
    int pin = parser.get_int("pin", -1);
    if (pin < 0) {
        std::cerr << "Error: --pin is required\n";
        return 1;
    }
    
    // Set up signal handlers
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    
    try {
        // Get chip number
        int chip_num = parser.get_int("chip", 0);
        
        // Create GPIO interface with specified chip
        auto gpio_interface = std::make_shared<GpiodGPIOInterface>();
        
        // Create GPIO button with parsed parameters
        TestableGPIOButton button(
            pin,
            parser.get_bool("pull-up", true),
            parser.get_bool("active-state", false),
            parser.get_double("bounce-time", 0.0),
            parser.get_double("hold-time", 1.0),
            parser.get_bool("hold-repeat", false),
            gpio_interface
        );
        
        // Configure the GPIO interface to use the specified chip
        if (!gpio_interface->open_gpio_chip(chip_num)) {
            std::cerr << "Error: Failed to open GPIO chip " << chip_num << " (/dev/gpiochip" << chip_num << ")\n";
            std::cerr << "Make sure the chip exists and you have proper permissions.\n";
            return 1;
        }
        
        // Set up event handlers
        std::string when_pressed = parser.get("when-pressed");
        if (!when_pressed.empty()) {
            button.set_when_pressed([when_pressed]() {
                run_command("when_pressed: " + when_pressed, when_pressed);
            });
        }
        
        std::string when_released = parser.get("when-released");
        if (!when_released.empty()) {
            button.set_when_released([when_released]() {
                run_command("when_released: " + when_released, when_released);
            });
        }
        
        std::string when_held = parser.get("when-held");
        if (!when_held.empty()) {
            button.set_when_held([when_held]() {
                run_command("when_held: " + when_held, when_held);
            });
        }
        
        // Start monitoring
        button.start();
        std::cerr << "Monitoring GPIO " << pin << std::endl;
        
        // Main loop - process GPIO events
        while (keep_running) {
            button.process_once();
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
        }
        
        button.stop();
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}
