// Suppress false positive from GCC 14.3+ about uninitialized std::function members
// This occurs in <regex> implementation which uses std::function internally
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif

#include "PatternMatcher.hh"
#include <algorithm>
#include <unordered_map>
#include <mutex>

namespace LDASTools {
namespace AL {

std::unordered_map<std::string, std::regex> PatternMatcher::regex_cache_;
std::mutex PatternMatcher::cache_mutex_;

bool PatternMatcher::matches(const std::string& pattern, const std::string& channel) {
    if (pattern.empty() || channel.empty()) {
        return false;
    }
    
    // Handle exact match
    if (pattern == channel) {
        return true;
    }
    
    // Handle wildcard patterns
    if (pattern.find('*') != std::string::npos || pattern.find('?') != std::string::npos) {
        const std::regex& regex = get_regex(pattern);
        return std::regex_match(channel, regex);
    }
    
    // Handle prefix matching (e.g., "framecpp" matches "framecpp.frtoc")
    if (channel.find(pattern) == 0) {
        // Check if it's a complete component match
        if (channel.length() == pattern.length() || channel[pattern.length()] == '.') {
            return true;
        }
    }
    
    return false;
}

std::vector<std::string> PatternMatcher::find_matching_channels(
    const std::string& pattern,
    const std::vector<std::string>& all_channels) {
    
    std::vector<std::string> matching;
    
    for (const auto& channel : all_channels) {
        if (matches(pattern, channel)) {
            matching.push_back(channel);
        }
    }
    
    return matching;
}

bool PatternMatcher::is_prefix_match(const std::string& pattern, const std::string& channel) {
    if (pattern.empty() || channel.empty()) {
        return false;
    }
    
    // Remove wildcards for prefix check
    std::string clean_pattern = pattern;
    size_t wildcard_pos = clean_pattern.find_first_of("*?");
    if (wildcard_pos != std::string::npos) {
        clean_pattern = clean_pattern.substr(0, wildcard_pos);
    }
    
    return channel.find(clean_pattern) == 0;
}

bool PatternMatcher::is_exact_match(const std::string& pattern, const std::string& channel) {
    return pattern == channel;
}

std::string PatternMatcher::pattern_to_regex(const std::string& pattern) {
    std::string regex_str;
    regex_str.reserve(pattern.size() * 2);
    
    regex_str += "^";  // Start of string
    
    for (char c : pattern) {
        switch (c) {
            case '*':
                // Match any characters except dot (for hierarchical boundaries)
                regex_str += "[^.]*";
                break;
            case '?':
                // Match single character except dot
                regex_str += "[^.]";
                break;
            case '.':
                // Escape dot for literal matching
                regex_str += "\\.";
                break;
            case '\\':
            case '^':
            case '$':
            case '[':
            case ']':
            case '(':
            case ')':
            case '{':
            case '}':
            case '+':
            case '|':
                // Escape special regex characters
                regex_str += '\\';
                regex_str += c;
                break;
            default:
                regex_str += c;
                break;
        }
    }
    
    regex_str += "$";  // End of string
    
    return regex_str;
}

const std::regex& PatternMatcher::get_regex(const std::string& pattern) {
    std::lock_guard<std::mutex> lock(cache_mutex_);
    
    auto it = regex_cache_.find(pattern);
    if (it != regex_cache_.end()) {
        return it->second;
    }
    
    // Compile and cache the regex
    std::string regex_str = pattern_to_regex(pattern);
    auto result = regex_cache_.emplace(pattern, std::regex(regex_str));
    return result.first->second;
}

} // namespace AL
} // namespace LDASTools

// Restore GCC diagnostic state
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif