#pragma once

#include <functional>
#include <string>
#include <memory>
#include <cstdint>

// Interface for GPIO operations to allow mocking in tests
class IGPIOInterface {
public:
    virtual ~IGPIOInterface() = default;
    
    // GPIO hardware operations (modern gpiod-style API)
    virtual bool open_gpio_chip(int chip_num = 0) = 0;
    virtual void close_gpio_chip() = 0;
    virtual bool request_gpio_line(int pin, bool pull_up = true) = 0;
    virtual void release_gpio_line(int pin) = 0;
    virtual bool read_pin_value(int pin) = 0;
    virtual bool wait_for_edge(int pin, int timeout_ms) = 0;
    
    // Time operations (for testing time-dependent behavior)
    virtual void sleep_ms(int milliseconds) = 0;
    virtual uint64_t get_time_ms() = 0;
};

// Legacy GPIO implementation using sysfs (deprecated but kept for compatibility)
class SysfsGPIOInterface : public IGPIOInterface {
public:
    // Legacy sysfs methods (not part of new gpiod API)
    bool export_gpio(int pin);
    bool unexport_gpio(int pin);
    bool set_direction(int pin, const std::string& direction);
    bool set_edge(int pin, const std::string& edge);
    int open_value_file(int pin);
    bool read_pin_value_fd(int fd);  // Renamed to avoid conflict
    void close_value_file(int fd);
    
    // IGPIOInterface implementation (stub - use GpiodGPIOInterface instead)
    bool open_gpio_chip(int chip_num = 0) override { return false; }
    void close_gpio_chip() override {}
    bool request_gpio_line(int pin, bool pull_up = true) override { return false; }
    void release_gpio_line(int pin) override {}
    bool read_pin_value(int pin) override { return false; }
    bool wait_for_edge(int pin, int timeout_ms) override { return false; }
    void sleep_ms(int milliseconds) override;
    uint64_t get_time_ms() override;
};

// Modern GPIO implementation using libgpiod v2 C++ bindings
class GpiodGPIOInterface : public IGPIOInterface {
private:
    struct Impl;
    std::unique_ptr<Impl> impl_;  // Pimpl to avoid including gpiod.hpp here

public:
    GpiodGPIOInterface();
    ~GpiodGPIOInterface();

    // Non-copyable, movable
    GpiodGPIOInterface(const GpiodGPIOInterface&) = delete;
    GpiodGPIOInterface& operator=(const GpiodGPIOInterface&) = delete;
    GpiodGPIOInterface(GpiodGPIOInterface&&) noexcept;
    GpiodGPIOInterface& operator=(GpiodGPIOInterface&&) noexcept;

    // GPIO operations
    bool open_gpio_chip(int chip_num = 0) override;
    void close_gpio_chip() override;
    bool request_gpio_line(int pin, bool pull_up = true) override;
    void release_gpio_line(int pin) override;
    bool read_pin_value(int pin) override;
    bool wait_for_edge(int pin, int timeout_ms) override;

    // Time operations
    void sleep_ms(int milliseconds) override;
    uint64_t get_time_ms() override;
};

// Testable GPIO Button class
class TestableGPIOButton {
private:
    int pin_;
    bool pull_up_;
    bool active_state_;
    double bounce_time_;
    double hold_time_;
    bool hold_repeat_;
    
    std::function<void()> when_pressed_;
    std::function<void()> when_released_;
    std::function<void()> when_held_;
    
    std::shared_ptr<IGPIOInterface> gpio_interface_;
    
    bool running_;
    bool gpio_chip_open_;
    bool last_state_;
    uint64_t press_time_;
    uint64_t last_bounce_time_;
    bool hold_triggered_;
    
public:
    TestableGPIOButton(int pin, bool pull_up = true, bool active_state = false,
                      double bounce_time = 0.0, double hold_time = 1.0, bool hold_repeat = false,
                      std::shared_ptr<IGPIOInterface> gpio_interface = nullptr);
    
    ~TestableGPIOButton();
    
    void set_when_pressed(std::function<void()> callback);
    void set_when_released(std::function<void()> callback);
    void set_when_held(std::function<void()> callback);
    
    bool start();
    void stop();
    
    // Test helper methods
    bool is_running() const { return running_; }
    bool get_last_state() const { return last_state_; }
    
    // Process one iteration of the monitor loop (for testing)
    void process_once();
    
private:
    bool setup_gpio();
    void cleanup_gpio();
    bool read_pin_state();
    void monitor_loop();
};
