#ifndef INIFILE_H
#define INIFILE_H

#include <fstream>
#include <map>
#include <string>
#include <cctype>
#include <limits>
#include <algorithm>
#include <vector>
#include <memory>
#include <cstdint>

class IniFile {
public:
    // Секции: section -> (key -> value)
    std::map<std::string, std::map<std::string, std::string> > sections;

    // Глобальные ключи (без секции)
    std::map<std::string, std::string>& global() {
        return sections[""];
    }

    bool read(const std::string& filename) {
        std::ifstream file(filename.c_str());
        if (!file.is_open()) return false;

        std::string line;
        std::string currentSection;

        while (std::getline(file, line)) {
            trim(line);

            size_t commentPos = line.find(';');
            if (commentPos != std::string::npos) {
                line = line.substr(0, commentPos);
                trim(line);
            }

            if (line.empty()) continue;

            // Секция [section]
            if (!line.empty() && line.front() == '[' && line.back() == ']') {
                currentSection = line.substr(1, line.size() - 2);
                trim(currentSection);
                sections[currentSection]; // создать секцию даже пустую
                continue;
            }

            // key=value
            size_t eqPos = line.find('=');
            if (eqPos == std::string::npos) {
                // Некорректная строка — пропускаем
                continue;
            }

            std::string key = line.substr(0, eqPos);
            std::string value = line.substr(eqPos + 1);

            trim(key);
            trim(value);

            if (!isValidKey(key)) {
                continue;
            }

            sections[currentSection][key] = value;
        }

        return true;
    }

    // --------- Базовый доступ ---------

    bool has(const std::string& key, const std::string& section = "") const {
        std::map<std::string, std::map<std::string, std::string> >::const_iterator sectionIt =
            sections.find(section);
        if (sectionIt == sections.end()) return false;

        return sectionIt->second.find(key) != sectionIt->second.end();
    }

    std::string get(const std::string& key, const std::string& section = "") const {
        std::map<std::string, std::map<std::string, std::string> >::const_iterator sectionIt =
            sections.find(section);
        if (sectionIt == sections.end()) return "";

        std::map<std::string, std::string>::const_iterator it = sectionIt->second.find(key);
        if (it != sectionIt->second.end()) return it->second;

        return "";
    }

    // C++11-friendly "optional": unique_ptr
    std::unique_ptr<std::string> tryGet(const std::string& key,
                                        const std::string& section = "") const {
        std::map<std::string, std::map<std::string, std::string> >::const_iterator sectionIt =
            sections.find(section);
        if (sectionIt == sections.end()) return std::unique_ptr<std::string>();

        std::map<std::string, std::string>::const_iterator it = sectionIt->second.find(key);
        if (it != sectionIt->second.end()) {
            return std::unique_ptr<std::string>(new std::string(it->second));
        }

        return std::unique_ptr<std::string>();
    }

    // --------- Typed getters ---------

    std::string toString(const std::string& key,
                         const std::string& defaultValue = "",
                         const std::string& section = "") const {
        std::unique_ptr<std::string> value = tryGet(key, section);
        return value ? *value : defaultValue;
    }

    bool toBool(const std::string& key,
                bool defaultValue = false,
                const std::string& section = "") const {
        std::unique_ptr<std::string> optValue = tryGet(key, section);
        if (!optValue) return defaultValue;

        std::string v = *optValue;
        trim(v);
        if (v.empty()) return defaultValue;

        toLowerInPlace(v);

        if (v == "1" || v == "true" || v == "yes" || v == "on" || v == "enabled")
            return true;
        if (v == "0" || v == "false" || v == "no" || v == "off" || v == "disabled")
            return false;

        try {
            int num = std::stoi(v);
            return num != 0;
        } catch (...) {
            return defaultValue;
        }
    }

    int32_t toInt32(const std::string& key,
                    int32_t defaultValue = 0,
                    const std::string& section = "") const {
        std::unique_ptr<std::string> optValue = tryGet(key, section);
        if (!optValue) return defaultValue;

        std::string v = *optValue;
        trim(v);
        if (v.empty()) return defaultValue;

        try {
            size_t pos = 0;
            long val = std::stol(v, &pos, 0); // base=0

            while (pos < v.size() && std::isspace(static_cast<unsigned char>(v[pos])))
                ++pos;
            if (pos != v.size()) return defaultValue;

            if (val < (std::numeric_limits<int32_t>::min)() ||
                val > (std::numeric_limits<int32_t>::max)()) {
                return defaultValue;
            }

            return static_cast<int32_t>(val);
        }
        catch (...) {
            return defaultValue;
        }
    }

    uint32_t toUint32(const std::string& key,
                      uint32_t defaultValue = 0,
                      const std::string& section = "") const {
        std::unique_ptr<std::string> optValue = tryGet(key, section);
        if (!optValue) return defaultValue;

        std::string v = *optValue;
        trim(v);
        if (v.empty()) return defaultValue;

        try {
            size_t pos = 0;
            unsigned long val = std::stoul(v, &pos, 0);

            while (pos < v.size() && std::isspace(static_cast<unsigned char>(v[pos])))
                ++pos;
            if (pos != v.size()) return defaultValue;

            if (val > (std::numeric_limits<uint32_t>::max)()) {
                return defaultValue;
            }

            return static_cast<uint32_t>(val);
        }
        catch (...) {
            return defaultValue;
        }
    }

    float toFloat(const std::string& key,
                  float defaultValue = 0.0f,
                  const std::string& section = "") const {
        std::unique_ptr<std::string> optValue = tryGet(key, section);
        if (!optValue) return defaultValue;

        std::string v = *optValue;
        trim(v);
        if (v.empty()) return defaultValue;

        try {
            size_t pos = 0;
            float val = std::stof(v, &pos);

            while (pos < v.size() && std::isspace(static_cast<unsigned char>(v[pos])))
                ++pos;
            if (pos != v.size()) return defaultValue;

            return val;
        }
        catch (...) {
            return defaultValue;
        }
    }

    std::vector<std::string> toArray(const std::string& key,
                                     char delimiter = ',',
                                     const std::string& section = "") const {
        std::unique_ptr<std::string> optValue = tryGet(key, section);
        if (!optValue) return std::vector<std::string>();

        std::string v = *optValue;
        trim(v);
        if (v.empty()) return std::vector<std::string>();

        std::vector<std::string> result;
        size_t start = 0;

        while (true) {
            size_t end = v.find(delimiter, start);
            std::string item = (end == std::string::npos)
                                   ? v.substr(start)
                                   : v.substr(start, end - start);

            trim(item);
            if (!item.empty()) result.push_back(item);

            if (end == std::string::npos) break;
            start = end + 1;
        }

        return result;
    }

    // --------- Запись ---------

    bool write(const std::string& filename) const {
        std::ofstream file(filename.c_str());
        if (!file.is_open()) return false;

        // Глобальная секция
        std::map<std::string, std::map<std::string, std::string> >::const_iterator globalIt =
            sections.find("");
        if (globalIt != sections.end() && !globalIt->second.empty()) {
            for (std::map<std::string, std::string>::const_iterator it = globalIt->second.begin();
                 it != globalIt->second.end(); ++it) {
                file << it->first << " = " << it->second << "\n";
            }
            file << "\n";
        }

        // Остальные секции
        for (std::map<std::string, std::map<std::string, std::string> >::const_iterator sit = sections.begin();
             sit != sections.end(); ++sit) {

            const std::string& sectionName = sit->first;
            const std::map<std::string, std::string>& sectionData = sit->second;

            if (sectionName.empty()) continue;
            if (sectionData.empty()) continue;

            file << "[" << sectionName << "]\n";
            for (std::map<std::string, std::string>::const_iterator it = sectionData.begin();
                 it != sectionData.end(); ++it) {
                file << it->first << " = " << it->second << "\n";
            }
            file << "\n";
        }

        return true;
    }

    // --------- Setters / remove ---------

    void set(const std::string& key, const std::string& value,
             const std::string& section = "") {
        sections[section][key] = value;
    }

    void setBool(const std::string& key, bool value,
                 const std::string& section = "") {
        sections[section][key] = value ? "true" : "false";
    }

    void setInt(const std::string& key, int32_t value,
                const std::string& section = "") {
        sections[section][key] = std::to_string(value);
    }

    bool remove(const std::string& key, const std::string& section = "") {
        std::map<std::string, std::map<std::string, std::string> >::iterator sectionIt =
            sections.find(section);
        if (sectionIt == sections.end()) return false;

        return sectionIt->second.erase(key) > 0;
    }

    bool removeSection(const std::string& section) {
        return sections.erase(section) > 0;
    }

    std::vector<std::string> getSections() const {
        std::vector<std::string> result;
        for (std::map<std::string, std::map<std::string, std::string> >::const_iterator it = sections.begin();
             it != sections.end(); ++it) {
            if (!it->first.empty()) result.push_back(it->first);
        }
        return result;
    }

    std::vector<std::string> getKeys(const std::string& section = "") const {
        std::vector<std::string> result;
        std::map<std::string, std::map<std::string, std::string> >::const_iterator sectionIt =
            sections.find(section);
        if (sectionIt != sections.end()) {
            for (std::map<std::string, std::string>::const_iterator it = sectionIt->second.begin();
                 it != sectionIt->second.end(); ++it) {
                result.push_back(it->first);
            }
        }
        return result;
    }

private:
    static void trim(std::string& str) {
        const char* WHITESPACE = " \t\r\n";

        size_t first = str.find_first_not_of(WHITESPACE);
        if (first == std::string::npos) {
            str.clear();
            return;
        }

        size_t last = str.find_last_not_of(WHITESPACE);
        str = str.substr(first, last - first + 1);
    }

    static void toLowerInPlace(std::string& s) {
        std::transform(s.begin(), s.end(), s.begin(),
                       [](unsigned char c) -> char {
                           return static_cast<char>(std::tolower(c));
                       }
                       );
    }

    static bool isValidKey(const std::string& key) {
        if (key.empty()) return false;

        // Ключ не должен содержать [] = ;
        for (size_t i = 0; i < key.size(); ++i) {
            char c = key[i];
            if (c == '[' || c == ']' || c == '=' || c == ';') return false;
        }

        return true;
    }
};


#endif // INIFILE_H
