#include "json_parser.h"
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <algorithm>

namespace includecpp {
namespace inbuilds {

// ===== HELPER FUNCTIONS =====

namespace {
    std::string trim(const std::string& str) {
        size_t first = str.find_first_not_of(" \t\n\r");
        if (first == std::string::npos) return "";
        size_t last = str.find_last_not_of(" \t\n\r");
        return str.substr(first, (last - first + 1));
    }

    std::string escape_string(const std::string& str) {
        std::string result;
        for (char c : str) {
            switch (c) {
                case '"': result += "\\\""; break;
                case '\\': result += "\\\\"; break;
                case '\b': result += "\\b"; break;
                case '\f': result += "\\f"; break;
                case '\n': result += "\\n"; break;
                case '\r': result += "\\r"; break;
                case '\t': result += "\\t"; break;
                default: result += c; break;
            }
        }
        return result;
    }

    std::string unescape_string(const std::string& str) {
        std::string result;
        bool escape = false;
        for (char c : str) {
            if (escape) {
                switch (c) {
                    case '"': result += '"'; break;
                    case '\\': result += '\\'; break;
                    case 'b': result += '\b'; break;
                    case 'f': result += '\f'; break;
                    case 'n': result += '\n'; break;
                    case 'r': result += '\r'; break;
                    case 't': result += '\t'; break;
                    default: result += c; break;
                }
                escape = false;
            } else if (c == '\\') {
                escape = true;
            } else {
                result += c;
            }
        }
        return result;
    }
}

// ===== PARSING =====

std::string JSON::parse(const std::string& json_str) {
    // Simply return trimmed JSON (validation)
    std::string trimmed = trim(json_str);
    if (trimmed.empty()) {
        throw std::runtime_error("Empty JSON string");
    }
    return trimmed;
}

std::string JSON::stringify(const std::string& data) {
    return data;
}

// ===== OBJECT OPERATIONS =====

std::string JSON::create_object() {
    return "{}";
}

std::string JSON::set_field(const std::string& obj, const std::string& key, const std::string& value) {
    std::string result = obj;

    // Remove closing brace
    if (result.back() == '}') {
        result.pop_back();
    }

    // Add comma if not empty
    std::string trimmed = trim(result);
    if (trimmed.length() > 1 && trimmed.back() != '{') {
        result += ",";
    }

    // Add new field
    result += "\"" + escape_string(key) + "\":" + value + "}";

    return result;
}

std::string JSON::get_field(const std::string& obj, const std::string& key) {
    // Simple key extraction (basic implementation)
    std::string search_key = "\"" + key + "\":";
    size_t pos = obj.find(search_key);

    if (pos == std::string::npos) {
        return "null";
    }

    pos += search_key.length();
    size_t end_pos = obj.find_first_of(",}", pos);

    if (end_pos == std::string::npos) {
        return "null";
    }

    return trim(obj.substr(pos, end_pos - pos));
}

bool JSON::has_field(const std::string& obj, const std::string& key) {
    return obj.find("\"" + key + "\":") != std::string::npos;
}

std::vector<std::string> JSON::get_keys(const std::string& obj) {
    std::vector<std::string> keys;
    size_t pos = 0;

    while ((pos = obj.find("\"", pos)) != std::string::npos) {
        pos++; // Skip opening quote
        size_t end_quote = obj.find("\"", pos);
        if (end_quote == std::string::npos) break;

        std::string potential_key = obj.substr(pos, end_quote - pos);

        // Check if followed by colon
        size_t colon_pos = obj.find(":", end_quote);
        if (colon_pos != std::string::npos && colon_pos - end_quote <= 2) {
            keys.push_back(potential_key);
        }

        pos = end_quote + 1;
    }

    return keys;
}

// ===== ARRAY OPERATIONS =====

std::string JSON::create_array() {
    return "[]";
}

std::string JSON::array_push(const std::string& arr, const std::string& value) {
    std::string result = arr;

    // Remove closing bracket
    if (result.back() == ']') {
        result.pop_back();
    }

    // Add comma if not empty
    std::string trimmed = trim(result);
    if (trimmed.length() > 1 && trimmed.back() != '[') {
        result += ",";
    }

    // Add new value
    result += value + "]";

    return result;
}

std::string JSON::array_get(const std::string& arr, int index) {
    // Simple array indexing (basic implementation)
    int current_index = 0;
    size_t pos = arr.find('[');
    if (pos == std::string::npos) return "null";
    pos++;

    while (current_index < index) {
        pos = arr.find(',', pos);
        if (pos == std::string::npos) return "null";
        pos++;
        current_index++;
    }

    size_t end_pos = arr.find_first_of(",]", pos);
    if (end_pos == std::string::npos) return "null";

    return trim(arr.substr(pos, end_pos - pos));
}

int JSON::array_length(const std::string& arr) {
    if (arr == "[]") return 0;

    int count = 1;
    for (char c : arr) {
        if (c == ',') count++;
    }
    return count;
}

// ===== TYPE CHECKING =====

bool JSON::is_object(const std::string& json) {
    std::string trimmed = trim(json);
    return !trimmed.empty() && trimmed.front() == '{' && trimmed.back() == '}';
}

bool JSON::is_array(const std::string& json) {
    std::string trimmed = trim(json);
    return !trimmed.empty() && trimmed.front() == '[' && trimmed.back() == ']';
}

bool JSON::is_string(const std::string& json) {
    std::string trimmed = trim(json);
    return !trimmed.empty() && trimmed.front() == '"' && trimmed.back() == '"';
}

bool JSON::is_number(const std::string& json) {
    std::string trimmed = trim(json);
    if (trimmed.empty()) return false;

    char first = trimmed[0];
    return (first >= '0' && first <= '9') || first == '-' || first == '+';
}

bool JSON::is_boolean(const std::string& json) {
    std::string trimmed = trim(json);
    return trimmed == "true" || trimmed == "false";
}

bool JSON::is_null(const std::string& json) {
    return trim(json) == "null";
}

// ===== CONVERSION =====

std::string JSON::to_pretty_string(const std::string& json) {
    std::string result;
    int indent = 0;
    bool in_string = false;

    for (size_t i = 0; i < json.length(); ++i) {
        char c = json[i];

        if (c == '"' && (i == 0 || json[i-1] != '\\')) {
            in_string = !in_string;
            result += c;
            continue;
        }

        if (in_string) {
            result += c;
            continue;
        }

        switch (c) {
            case '{':
            case '[':
                result += c;
                result += '\n';
                indent += 2;
                result += std::string(indent, ' ');
                break;
            case '}':
            case ']':
                result += '\n';
                indent -= 2;
                result += std::string(indent, ' ');
                result += c;
                break;
            case ',':
                result += c;
                result += '\n';
                result += std::string(indent, ' ');
                break;
            case ':':
                result += c;
                result += ' ';
                break;
            default:
                if (c != ' ' && c != '\n' && c != '\r' && c != '\t') {
                    result += c;
                }
                break;
        }
    }

    return result;
}

std::string JSON::from_string(const std::string& str) {
    return "\"" + escape_string(str) + "\"";
}

std::string JSON::from_number(double num) {
    return std::to_string(num);
}

std::string JSON::from_boolean(bool val) {
    return val ? "true" : "false";
}

std::string JSON::from_null() {
    return "null";
}

// ===== FREE FUNCTIONS =====

std::string load_json(const std::string& filepath) {
    std::ifstream file(filepath);
    if (!file.is_open()) {
        throw std::runtime_error("Cannot open file: " + filepath);
    }

    std::ostringstream ss;
    ss << file.rdbuf();
    return JSON::parse(ss.str());
}

bool save_json(const std::string& filepath, const std::string& json) {
    std::ofstream file(filepath);
    if (!file.is_open()) {
        return false;
    }

    file << JSON::to_pretty_string(json);
    return file.good();
}

} // namespace inbuilds
} // namespace includecpp
