#pragma once

#include <algorithm>
#include <cctype>
#include <cstddef>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>

namespace ta::foundation::text
{
    inline std::vector<std::string> split(const std::string& value, const char delimiter)
    {
        std::vector<std::string> parts;
        std::size_t start = 0;
        while (start <= value.size())
        {
            const std::size_t end = value.find(delimiter, start);
            if (end == std::string::npos)
            {
                parts.push_back(value.substr(start));
                break;
            }
            parts.push_back(value.substr(start, end - start));
            start = end + 1;
        }
        return parts;
    }

    inline std::size_t utf8_bom_size(const std::string& text) noexcept
    {
        return text.size() >= 3 &&
               static_cast<unsigned char>(text[0]) == 0xEF &&
               static_cast<unsigned char>(text[1]) == 0xBB &&
               static_cast<unsigned char>(text[2]) == 0xBF
            ? 3U
            : 0U;
    }

    inline std::string trim_copy(const std::string& value)
    {
        const auto begin = std::find_if_not(value.begin(), value.end(), [](const unsigned char c)
        {
            return std::isspace(c) != 0;
        });
        const auto end = std::find_if_not(value.rbegin(), value.rend(), [](const unsigned char c)
        {
            return std::isspace(c) != 0;
        }).base();

        if (begin >= end) return {};
        return std::string(begin, end);
    }

    inline std::string lower_copy(std::string value)
    {
        std::ranges::transform(value, value.begin(), [](const unsigned char c)
        {
            return static_cast<char>(std::tolower(c));
        });
        return value;
    }

    inline bool starts_with(const std::string& value, const std::string& prefix)
    {
        return value.rfind(prefix, 0) == 0;
    }

    inline bool ends_with(const std::string& value, const std::string& suffix)
    {
        return value.size() >= suffix.size() &&
               value.compare(value.size() - suffix.size(), suffix.size(), suffix) == 0;
    }

    inline bool case_equal(const std::string& left, const std::string& right)
    {
        return lower_copy(left) == lower_copy(right);
    }

    inline std::string strip_suffix(const std::string& value, const std::string& suffix)
    {
        return ends_with(value, suffix) ? value.substr(0, value.size() - suffix.size()) : value;
    }

    inline void dedupe_case_insensitive(std::vector<std::string>& values)
    {
        std::unordered_set<std::string> seen;
        std::vector<std::string> unique;
        unique.reserve(values.size());
        for (const std::string& value : values)
        {
            const std::string key = lower_copy(value);
            if (seen.insert(key).second)
                unique.push_back(value);
        }
        values = std::move(unique);
    }
}
