#include "string_utils.h"
#include <algorithm>
#include <cctype>
#include <sstream>
#include <iomanip>
#include <random>
#include <functional>

namespace includecpp {
namespace inbuilds {

// ===== BASIC OPERATIONS =====

std::string StringUtils::to_upper(const std::string& str) {
    std::string result = str;
    std::transform(result.begin(), result.end(), result.begin(), ::toupper);
    return result;
}

std::string StringUtils::to_lower(const std::string& str) {
    std::string result = str;
    std::transform(result.begin(), result.end(), result.begin(), ::tolower);
    return result;
}

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

std::string StringUtils::trim_left(const std::string& str) {
    size_t first = str.find_first_not_of(" \t\n\r\f\v");
    if (first == std::string::npos) return "";
    return str.substr(first);
}

std::string StringUtils::trim_right(const std::string& str) {
    size_t last = str.find_last_not_of(" \t\n\r\f\v");
    if (last == std::string::npos) return "";
    return str.substr(0, last + 1);
}

std::string StringUtils::reverse(const std::string& str) {
    return std::string(str.rbegin(), str.rend());
}

std::string StringUtils::repeat(const std::string& str, int count) {
    std::string result;
    result.reserve(str.length() * count);
    for (int i = 0; i < count; ++i) {
        result += str;
    }
    return result;
}

std::string StringUtils::pad_left(const std::string& str, int width, char fill) {
    if (static_cast<int>(str.length()) >= width) return str;
    return std::string(width - str.length(), fill) + str;
}

std::string StringUtils::pad_right(const std::string& str, int width, char fill) {
    if (static_cast<int>(str.length()) >= width) return str;
    return str + std::string(width - str.length(), fill);
}

std::string StringUtils::pad_center(const std::string& str, int width, char fill) {
    if (static_cast<int>(str.length()) >= width) return str;
    int total_padding = width - str.length();
    int left_padding = total_padding / 2;
    int right_padding = total_padding - left_padding;
    return std::string(left_padding, fill) + str + std::string(right_padding, fill);
}

// ===== SEARCH & REPLACE =====

bool StringUtils::contains(const std::string& str, const std::string& substr) {
    return str.find(substr) != std::string::npos;
}

bool StringUtils::starts_with(const std::string& str, const std::string& prefix) {
    if (prefix.length() > str.length()) return false;
    return str.compare(0, prefix.length(), prefix) == 0;
}

bool StringUtils::ends_with(const std::string& str, const std::string& suffix) {
    if (suffix.length() > str.length()) return false;
    return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
}

int StringUtils::index_of(const std::string& str, const std::string& substr, int start) {
    size_t pos = str.find(substr, start);
    return pos == std::string::npos ? -1 : static_cast<int>(pos);
}

int StringUtils::last_index_of(const std::string& str, const std::string& substr) {
    size_t pos = str.rfind(substr);
    return pos == std::string::npos ? -1 : static_cast<int>(pos);
}

int StringUtils::count_occurrences(const std::string& str, const std::string& substr) {
    if (substr.empty()) return 0;
    int count = 0;
    size_t pos = 0;
    while ((pos = str.find(substr, pos)) != std::string::npos) {
        ++count;
        pos += substr.length();
    }
    return count;
}

std::string StringUtils::replace(const std::string& str, const std::string& old_str, const std::string& new_str) {
    return replace_all(str, old_str, new_str);
}

std::string StringUtils::replace_all(const std::string& str, const std::string& old_str, const std::string& new_str) {
    if (old_str.empty()) return str;
    std::string result = str;
    size_t pos = 0;
    while ((pos = result.find(old_str, pos)) != std::string::npos) {
        result.replace(pos, old_str.length(), new_str);
        pos += new_str.length();
    }
    return result;
}

std::string StringUtils::replace_first(const std::string& str, const std::string& old_str, const std::string& new_str) {
    size_t pos = str.find(old_str);
    if (pos == std::string::npos) return str;
    std::string result = str;
    result.replace(pos, old_str.length(), new_str);
    return result;
}

std::string StringUtils::replace_last(const std::string& str, const std::string& old_str, const std::string& new_str) {
    size_t pos = str.rfind(old_str);
    if (pos == std::string::npos) return str;
    std::string result = str;
    result.replace(pos, old_str.length(), new_str);
    return result;
}

// ===== SPLITTING & JOINING =====

std::vector<std::string> StringUtils::split(const std::string& str, const std::string& delimiter) {
    std::vector<std::string> tokens;
    size_t start = 0;
    size_t end = str.find(delimiter);

    while (end != std::string::npos) {
        tokens.push_back(str.substr(start, end - start));
        start = end + delimiter.length();
        end = str.find(delimiter, start);
    }

    tokens.push_back(str.substr(start));
    return tokens;
}

std::vector<std::string> StringUtils::split_lines(const std::string& str) {
    std::vector<std::string> lines;
    std::istringstream stream(str);
    std::string line;

    while (std::getline(stream, line)) {
        lines.push_back(line);
    }

    return lines;
}

std::vector<std::string> StringUtils::split_whitespace(const std::string& str) {
    std::vector<std::string> tokens;
    std::istringstream stream(str);
    std::string word;

    while (stream >> word) {
        tokens.push_back(word);
    }

    return tokens;
}

std::string StringUtils::join(const std::vector<std::string>& strings, const std::string& delimiter) {
    if (strings.empty()) return "";

    std::string result = strings[0];
    for (size_t i = 1; i < strings.size(); ++i) {
        result += delimiter + strings[i];
    }

    return result;
}

std::vector<std::string> StringUtils::chunk(const std::string& str, int size) {
    std::vector<std::string> chunks;
    for (size_t i = 0; i < str.length(); i += size) {
        chunks.push_back(str.substr(i, size));
    }
    return chunks;
}

// ===== CHARACTER CLASSIFICATION =====

bool StringUtils::is_alpha(const std::string& str) {
    if (str.empty()) return false;
    return std::all_of(str.begin(), str.end(), [](unsigned char c) { return std::isalpha(c); });
}

bool StringUtils::is_numeric(const std::string& str) {
    if (str.empty()) return false;
    return std::all_of(str.begin(), str.end(), [](unsigned char c) { return std::isdigit(c); });
}

bool StringUtils::is_alphanumeric(const std::string& str) {
    if (str.empty()) return false;
    return std::all_of(str.begin(), str.end(), [](unsigned char c) { return std::isalnum(c); });
}

bool StringUtils::is_lowercase(const std::string& str) {
    if (str.empty()) return false;
    return std::all_of(str.begin(), str.end(), [](unsigned char c) { return !std::isalpha(c) || std::islower(c); });
}

bool StringUtils::is_uppercase(const std::string& str) {
    if (str.empty()) return false;
    return std::all_of(str.begin(), str.end(), [](unsigned char c) { return !std::isalpha(c) || std::isupper(c); });
}

bool StringUtils::is_whitespace(const std::string& str) {
    if (str.empty()) return false;
    return std::all_of(str.begin(), str.end(), [](unsigned char c) { return std::isspace(c); });
}

bool StringUtils::is_empty(const std::string& str) {
    return str.empty();
}

bool StringUtils::is_blank(const std::string& str) {
    return trim(str).empty();
}

// ===== REGEX OPERATIONS =====

bool StringUtils::regex_match(const std::string& str, const std::string& pattern) {
    try {
        std::regex re(pattern);
        return std::regex_match(str, re);
    } catch (...) {
        return false;
    }
}

std::vector<std::string> StringUtils::regex_find_all(const std::string& str, const std::string& pattern) {
    std::vector<std::string> matches;
    try {
        std::regex re(pattern);
        auto begin = std::sregex_iterator(str.begin(), str.end(), re);
        auto end = std::sregex_iterator();

        for (auto it = begin; it != end; ++it) {
            matches.push_back(it->str());
        }
    } catch (...) {
        // Return empty vector on error
    }
    return matches;
}

std::string StringUtils::regex_replace(const std::string& str, const std::string& pattern, const std::string& replacement) {
    try {
        std::regex re(pattern);
        return std::regex_replace(str, re, replacement);
    } catch (...) {
        return str;
    }
}

std::vector<std::string> StringUtils::regex_split(const std::string& str, const std::string& pattern) {
    std::vector<std::string> tokens;
    try {
        std::regex re(pattern);
        std::sregex_token_iterator iter(str.begin(), str.end(), re, -1);
        std::sregex_token_iterator end;

        for (; iter != end; ++iter) {
            tokens.push_back(*iter);
        }
    } catch (...) {
        tokens.push_back(str);
    }
    return tokens;
}

// ===== SUBSTRING OPERATIONS =====

std::string StringUtils::substring(const std::string& str, int start, int length) {
    if (start < 0 || start >= static_cast<int>(str.length())) return "";
    if (length < 0) return str.substr(start);
    return str.substr(start, length);
}

std::string StringUtils::left(const std::string& str, int length) {
    if (length < 0 || length >= static_cast<int>(str.length())) return str;
    return str.substr(0, length);
}

std::string StringUtils::right(const std::string& str, int length) {
    if (length < 0 || length >= static_cast<int>(str.length())) return str;
    return str.substr(str.length() - length);
}

std::string StringUtils::remove(const std::string& str, int start, int length) {
    if (start < 0 || start >= static_cast<int>(str.length())) return str;
    std::string result = str;
    result.erase(start, length);
    return result;
}

std::string StringUtils::insert(const std::string& str, int pos, const std::string& substr) {
    if (pos < 0 || pos > static_cast<int>(str.length())) return str;
    std::string result = str;
    result.insert(pos, substr);
    return result;
}

// ===== ENCODING/DECODING =====

std::string StringUtils::url_encode(const std::string& str) {
    std::ostringstream escaped;
    escaped.fill('0');
    escaped << std::hex;

    for (unsigned char c : str) {
        if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
            escaped << c;
        } else {
            escaped << '%' << std::setw(2) << static_cast<int>(c);
        }
    }

    return escaped.str();
}

std::string StringUtils::url_decode(const std::string& str) {
    std::string result;
    for (size_t i = 0; i < str.length(); ++i) {
        if (str[i] == '%') {
            if (i + 2 < str.length()) {
                std::string hex = str.substr(i + 1, 2);
                char decoded = static_cast<char>(std::stoi(hex, nullptr, 16));
                result += decoded;
                i += 2;
            }
        } else if (str[i] == '+') {
            result += ' ';
        } else {
            result += str[i];
        }
    }
    return result;
}

std::string StringUtils::html_escape(const std::string& str) {
    std::string result;
    for (char c : str) {
        switch (c) {
            case '&': result += "&amp;"; break;
            case '<': result += "&lt;"; break;
            case '>': result += "&gt;"; break;
            case '"': result += "&quot;"; break;
            case '\'': result += "&#39;"; break;
            default: result += c; break;
        }
    }
    return result;
}

std::string StringUtils::html_unescape(const std::string& str) {
    std::string result = str;
    result = replace_all(result, "&amp;", "&");
    result = replace_all(result, "&lt;", "<");
    result = replace_all(result, "&gt;", ">");
    result = replace_all(result, "&quot;", "\"");
    result = replace_all(result, "&#39;", "'");
    return result;
}

// ===== CASE CONVERSION =====

std::string StringUtils::to_title_case(const std::string& str) {
    std::string result = to_lower(str);
    bool capitalize_next = true;

    for (char& c : result) {
        if (std::isspace(c)) {
            capitalize_next = true;
        } else if (capitalize_next) {
            c = std::toupper(c);
            capitalize_next = false;
        }
    }

    return result;
}

std::string StringUtils::to_camel_case(const std::string& str) {
    std::vector<std::string> words = split_whitespace(str);
    if (words.empty()) return "";

    std::string result = to_lower(words[0]);
    for (size_t i = 1; i < words.size(); ++i) {
        if (!words[i].empty()) {
            result += static_cast<char>(std::toupper(words[i][0]));
            result += to_lower(words[i].substr(1));
        }
    }

    return result;
}

std::string StringUtils::to_snake_case(const std::string& str) {
    std::string result;
    bool prev_was_upper = false;

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

        if (std::isupper(c)) {
            if (i > 0 && !prev_was_upper && result.back() != '_') {
                result += '_';
            }
            result += std::tolower(c);
            prev_was_upper = true;
        } else if (std::isspace(c) || c == '-') {
            if (!result.empty() && result.back() != '_') {
                result += '_';
            }
            prev_was_upper = false;
        } else {
            result += c;
            prev_was_upper = false;
        }
    }

    return result;
}

std::string StringUtils::to_kebab_case(const std::string& str) {
    std::string snake = to_snake_case(str);
    return replace_all(snake, "_", "-");
}

std::string StringUtils::to_pascal_case(const std::string& str) {
    std::vector<std::string> words = split_whitespace(str);
    std::string result;

    for (const auto& word : words) {
        if (!word.empty()) {
            result += static_cast<char>(std::toupper(word[0]));
            result += to_lower(word.substr(1));
        }
    }

    return result;
}

// ===== COMPARISON =====

bool StringUtils::equals_ignore_case(const std::string& a, const std::string& b) {
    return to_lower(a) == to_lower(b);
}

int StringUtils::compare_natural(const std::string& a, const std::string& b) {
    size_t i = 0, j = 0;

    while (i < a.length() && j < b.length()) {
        if (std::isdigit(a[i]) && std::isdigit(b[j])) {
            std::string num_a, num_b;

            while (i < a.length() && std::isdigit(a[i])) {
                num_a += a[i++];
            }

            while (j < b.length() && std::isdigit(b[j])) {
                num_b += b[j++];
            }

            int diff = std::stoi(num_a) - std::stoi(num_b);
            if (diff != 0) return diff;
        } else {
            if (a[i] != b[j]) {
                return a[i] - b[j];
            }
            ++i;
            ++j;
        }
    }

    return static_cast<int>(a.length()) - static_cast<int>(b.length());
}

double StringUtils::similarity(const std::string& a, const std::string& b) {
    int max_len = std::max(a.length(), b.length());
    if (max_len == 0) return 1.0;

    int distance = levenshtein_distance(a, b);
    return 1.0 - (static_cast<double>(distance) / max_len);
}

int StringUtils::levenshtein_distance(const std::string& a, const std::string& b) {
    const size_t m = a.length();
    const size_t n = b.length();

    if (m == 0) return static_cast<int>(n);
    if (n == 0) return static_cast<int>(m);

    std::vector<std::vector<int>> dp(m + 1, std::vector<int>(n + 1));

    for (size_t i = 0; i <= m; ++i) {
        dp[i][0] = static_cast<int>(i);
    }

    for (size_t j = 0; j <= n; ++j) {
        dp[0][j] = static_cast<int>(j);
    }

    for (size_t i = 1; i <= m; ++i) {
        for (size_t j = 1; j <= n; ++j) {
            int cost = (a[i - 1] == b[j - 1]) ? 0 : 1;
            dp[i][j] = std::min({
                dp[i - 1][j] + 1,      // deletion
                dp[i][j - 1] + 1,      // insertion
                dp[i - 1][j - 1] + cost // substitution
            });
        }
    }

    return dp[m][n];
}

// ===== FORMATTING =====

std::string StringUtils::format(const std::string& fmt, const std::vector<std::string>& args) {
    std::string result = fmt;
    for (size_t i = 0; i < args.size(); ++i) {
        std::string placeholder = "{" + std::to_string(i) + "}";
        result = replace_all(result, placeholder, args[i]);
    }
    return result;
}

std::string StringUtils::truncate(const std::string& str, int max_length, const std::string& suffix) {
    if (static_cast<int>(str.length()) <= max_length) return str;
    return str.substr(0, max_length - suffix.length()) + suffix;
}

std::string StringUtils::wrap(const std::string& str, int width) {
    std::vector<std::string> words = split_whitespace(str);
    std::string result;
    std::string line;

    for (const auto& word : words) {
        if (line.length() + word.length() + 1 > static_cast<size_t>(width)) {
            if (!result.empty()) result += '\n';
            result += line;
            line = word;
        } else {
            if (!line.empty()) line += ' ';
            line += word;
        }
    }

    if (!line.empty()) {
        if (!result.empty()) result += '\n';
        result += line;
    }

    return result;
}

std::string StringUtils::remove_accents(const std::string& str) {
    // TODO: Implement proper Unicode normalization
    // For now, returns input unchanged as UTF-8 accent removal requires Unicode libraries
    return str;
}

// ===== VALIDATION =====

bool StringUtils::is_email(const std::string& str) {
    std::regex email_regex(R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)");
    return std::regex_match(str, email_regex);
}

bool StringUtils::is_url(const std::string& str) {
    std::regex url_regex(R"(^https?://[^\s/$.?#].[^\s]*$)", std::regex::icase);
    return std::regex_match(str, url_regex);
}

bool StringUtils::is_ip_address(const std::string& str) {
    std::regex ip_regex(R"(^(\d{1,3}\.){3}\d{1,3}$)");
    if (!std::regex_match(str, ip_regex)) return false;

    auto parts = split(str, ".");
    for (const auto& part : parts) {
        int num = std::stoi(part);
        if (num < 0 || num > 255) return false;
    }

    return true;
}

bool StringUtils::is_hex(const std::string& str) {
    if (str.empty()) return false;
    return std::all_of(str.begin(), str.end(), [](char c) {
        return std::isxdigit(static_cast<unsigned char>(c));
    });
}

bool StringUtils::is_base64(const std::string& str) {
    std::regex base64_regex(R"(^[A-Za-z0-9+/]*={0,2}$)");
    return std::regex_match(str, base64_regex) && str.length() % 4 == 0;
}

// ===== UTILITIES =====

std::string StringUtils::random_string(int length, const std::string& charset) {
    static const std::string default_charset =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    const std::string& chars = charset.empty() ? default_charset : charset;

    static std::random_device rd;
    static std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, chars.length() - 1);

    std::string result;
    result.reserve(length);

    for (int i = 0; i < length; ++i) {
        result += chars[dis(gen)];
    }

    return result;
}

std::string StringUtils::hash_string(const std::string& str) {
    std::hash<std::string> hasher;
    size_t hash = hasher(str);
    std::ostringstream ss;
    ss << std::hex << hash;
    return ss.str();
}

std::vector<std::string> StringUtils::extract_numbers(const std::string& str) {
    std::vector<std::string> numbers;
    std::regex number_regex(R"(-?\d+\.?\d*)");

    auto begin = std::sregex_iterator(str.begin(), str.end(), number_regex);
    auto end = std::sregex_iterator();

    for (auto it = begin; it != end; ++it) {
        numbers.push_back(it->str());
    }

    return numbers;
}

std::vector<std::string> StringUtils::extract_words(const std::string& str) {
    std::vector<std::string> words;
    std::regex word_regex(R"(\b[a-zA-Z]+\b)");

    auto begin = std::sregex_iterator(str.begin(), str.end(), word_regex);
    auto end = std::sregex_iterator();

    for (auto it = begin; it != end; ++it) {
        words.push_back(it->str());
    }

    return words;
}

std::map<std::string, int> StringUtils::word_frequency(const std::string& str) {
    std::map<std::string, int> frequency;
    auto words = extract_words(str);

    for (const auto& word : words) {
        std::string lower_word = to_lower(word);
        frequency[lower_word]++;
    }

    return frequency;
}

} // namespace inbuilds
} // namespace includecpp
