#pragma once
#include <string>
#include <cstring>
#include <cstdint>
#include <cstdlib>
#include <algorithm>
#include <sstream>
#include <cctype>

// Minimal String class that mimics Arduino's String for native tests
struct String {
    std::string _s;

    String() {}
    String(const char* s) : _s(s ? s : "") {}
    String(const std::string& s) : _s(s) {}
    String(int v) { _s = std::to_string(v); }
    String(unsigned long v) { _s = std::to_string(v); }
    String(long long v) { _s = std::to_string(v); }
    String(unsigned long long v) { _s = std::to_string(v); }

    const char* c_str() const { return _s.c_str(); }
    size_t length() const { return _s.size(); }
    bool isEmpty() const { return _s.empty(); }
    bool empty() const { return _s.empty(); }

    int toInt() const { return _s.empty() ? 0 : std::stoi(_s); }

    String substring(size_t from) const { return String(_s.substr(from)); }
    String substring(size_t from, size_t to) const { return String(_s.substr(from, to - from)); }

    void replace(const char* from, const char* to_str) {
        std::string result;
        std::string f(from), t(to_str);
        size_t pos = 0, found;
        while ((found = _s.find(f, pos)) != std::string::npos) {
            result += _s.substr(pos, found - pos);
            result += t;
            pos = found + f.size();
        }
        result += _s.substr(pos);
        _s = result;
    }

    void toUpperCase() {
        for (char& c : _s) c = (char)toupper((unsigned char)c);
    }

    bool equalsIgnoreCase(const String& o) const {
        if (_s.size() != o._s.size()) return false;
        for (size_t i = 0; i < _s.size(); i++) {
            if (tolower((unsigned char)_s[i]) != tolower((unsigned char)o._s[i])) return false;
        }
        return true;
    }

    bool operator==(const String& o) const { return _s == o._s; }
    bool operator==(const char* o) const { return _s == o; }
    bool operator!=(const String& o) const { return _s != o._s; }
    bool operator!=(const char* o) const { return _s != o; }

    String operator+(const String& o) const { return String(_s + o._s); }
    String operator+(const char* o) const { return String(_s + o); }
    String& operator+=(const String& o) { _s += o._s; return *this; }
    String& operator+=(const char* o) { _s += o; return *this; }

    // Allow use as map key
    operator std::string() const { return _s; }

    // toString() for IP-like objects that have it
    String toString() const { return *this; }
};

inline String operator+(const char* a, const String& b) { return String(std::string(a) + b._s); }
inline String operator+(const std::string& a, const String& b) { return String(a + b._s); }

// Controllable millis and digitalRead for timeout / button tests
extern uint32_t g_millis_value;
extern int      g_digital_read_value;

#ifndef LOW
#define LOW  0
#define HIGH 1
#endif

inline unsigned long millis() { return g_millis_value += 10; }
inline void delay(unsigned long) {}
inline void pinMode(int, int) {}
inline int digitalRead(int) { return g_digital_read_value; }

// Color constants (from config.h)
#define COLOR_YELLOW 0x2
#define COLOR_RED    0x3

// Serial mock
struct SerialMock {
    void begin(int) {}
    void println(const String&) {}
    void println(const char*) {}
    void println(int) {}
    void print(const String&) {}
    void print(const char*) {}
    void flush() {}
} Serial;

// strtoull is available from <cstdlib> on native
