#include "CryptocurrencyValidator.hpp"

static const std::wstring kBase58Characters = L"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
static const std::wstring kHexCharacters = L"0123456789abcdefABCDEF";
static const std::wstring kBech32Characters = L"0123456789abcdefghijklmnopqrstuvwxyz";

bool CryptocurrencyValidator::Validate(Cryptocurrency type, const std::wstring& address)
{
    if (address.empty())
        return false;

    switch (type)
    {
        case Cryptocurrency::Bitcoin:
            return ValidateBitcoin(address);

        case Cryptocurrency::EVM:
            return ValidateEVM(address);

        case Cryptocurrency::Monero:
            return ValidateMonero(address);

        case Cryptocurrency::Litecoin:
            return ValidateLitecoin(address);

        case Cryptocurrency::Tron:
            return ValidateTron(address);

        case Cryptocurrency::Solana:
            return ValidateSolana(address);

        case Cryptocurrency::Ripple:
            return ValidateRipple(address);

        case Cryptocurrency::Dogecoin:
            return ValidateDogecoin(address);

        default:
            return false;
    }
}

bool CryptocurrencyValidator::ValidateBitcoin(const std::wstring& address)
{
    if (ValidateBitcoinLegacy(address))
        return true;

    if (ValidateBitcoinSegWit(address))
        return true;

    if (ValidateBitcoinNative(address))
        return true;

    return false;
}

bool CryptocurrencyValidator::ValidateBitcoinLegacy(const std::wstring& address)
{
    if (!CheckBitcoinLegacyPrefix(address))
        return false;

    if (!CheckAddressLength(address, 26, 35))
        return false;

    if (!CheckBase58Format(address))
        return false;

    return true;
}

bool CryptocurrencyValidator::ValidateBitcoinSegWit(const std::wstring& address)
{
    if (!CheckBitcoinSegWitPrefix(address))
        return false;

    if (!CheckAddressLength(address, 26, 35))
        return false;

    if (!CheckBase58Format(address))
        return false;

    return true;
}

bool CryptocurrencyValidator::ValidateBitcoinNative(const std::wstring& address)
{
    if (address.length() < 42 || address.length() > 62)
        return false;

    if (address.substr(0, 3) != L"bc1")
        return false;

    static const std::wregex pattern(L"^bc1[a-zA-HJ-NP-Z0-9]{39,59}$");
    return MatchesPattern(address, pattern);
}

bool CryptocurrencyValidator::ValidateEVM(const std::wstring& address)
{
    if (address.length() != 42)
        return false;

    if (address.substr(0, 2) != L"0x" && address.substr(0, 2) != L"0X")
        return false;

    std::wstring hexPart = address.substr(2);

    if (!CheckHexFormat(hexPart))
        return false;

    if (hexPart.length() != 40)
        return false;

    return true;
}

bool CryptocurrencyValidator::ValidateMonero(const std::wstring& address)
{
    if (address.length() != 95 && address.length() != 106)
        return false;

    wchar_t firstChar = address[0];
    if (firstChar != L'4' && firstChar != L'8')
        return false;

    if (!CheckBase58Format(address))
        return false;

    static const std::wregex pattern(L"^[48][1-9A-HJ-NP-Za-km-z]{94}|^[48][1-9A-HJ-NP-Za-km-z]{105}$");
    return std::regex_match(address, pattern);
}

bool CryptocurrencyValidator::ValidateLitecoin(const std::wstring& address)
{
    if (ValidateLitecoinLegacy(address))
        return true;

    if (ValidateLitecoinSegWit(address))
        return true;

    return false;
}

bool CryptocurrencyValidator::ValidateLitecoinLegacy(const std::wstring& address)
{
    if (!CheckLitecoinLegacyPrefix(address))
        return false;

    if (!CheckAddressLength(address, 27, 34))
        return false;

    if (!CheckBase58Format(address))
        return false;

    return true;
}

bool CryptocurrencyValidator::ValidateLitecoinSegWit(const std::wstring& address)
{
    if (address.length() < 11 || address.length() > 90)
        return false;

    if (address.substr(0, 4) != L"ltc1")
        return false;

    static const std::wregex pattern(L"^ltc1[a-zA-HJ-NP-Z0-9]{8,87}$");
    return MatchesPattern(address, pattern);
}

bool CryptocurrencyValidator::ValidateTron(const std::wstring& address)
{
    if (!CheckTronPrefix(address))
        return false;

    if (address.length() != 34)
        return false;

    if (!CheckBase58Format(address))
        return false;

    static const std::wregex pattern(L"^T[1-9A-HJ-NP-Za-km-z]{33}$");
    return MatchesPattern(address, pattern);
}

bool CryptocurrencyValidator::ValidateSolana(const std::wstring& address)
{
    if (!CheckAddressLength(address, 32, 44))
        return false;

    wchar_t firstChar = address[0];
    if (firstChar == L'r')  // XRP
        return false;

    if (firstChar == L'D')  // DOGE
        return false;

    if (firstChar == L'T')  // TRON
        return false;

    if (firstChar == L'4' || firstChar == L'8')  // Monero
        return false;

    if (firstChar == L'1' || firstChar == L'3')  // Bitcoin legacy
        return false;

    if (firstChar == L'L' || firstChar == L'M')  // Litecoin
        return false;

    if (firstChar == L'0')  // EVM
        return false;

    if (!CheckBase58Format(address))
        return false;

    static const std::wregex pattern(L"^[1-9A-HJ-NP-Za-km-z]{32,44}$");
    return MatchesPattern(address, pattern);
}

bool CryptocurrencyValidator::ValidateRipple(const std::wstring& address)
{
    if (address.empty() || address[0] != L'r')
        return false;

    if (!CheckAddressLength(address, 25, 35))
        return false;

    if (!CheckBase58Format(address))
        return false;

    static const std::wregex pattern(L"^r[0-9a-zA-Z]{24,34}$");
    return MatchesPattern(address, pattern);
}

bool CryptocurrencyValidator::ValidateDogecoin(const std::wstring& address)
{
    if (!CheckDogecoinPrefix(address))
        return false;

    if (address.length() != 34)
        return false;

    if (address[0] != L'D')
        return false;

    wchar_t secondChar = address[1];
    if (!(secondChar >= L'5' && secondChar <= L'9') &&
        !(secondChar >= L'A' && secondChar <= L'H') &&
        !(secondChar >= L'J' && secondChar <= L'N') &&
        !(secondChar >= L'P' && secondChar <= L'U'))
        return false;

    if (!CheckBase58Format(address))
        return false;

    static const std::wregex pattern(L"^D[5-9A-HJ-NP-U][1-9A-HJ-NP-Za-km-z]{32}$");
    return MatchesPattern(address, pattern);
}

bool CryptocurrencyValidator::CheckBitcoinLegacyPrefix(const std::wstring& address)
{
    if (address.empty())
        return false;

    wchar_t firstChar = address[0];
    return firstChar == L'1' || firstChar == L'3';
}

bool CryptocurrencyValidator::CheckBitcoinSegWitPrefix(const std::wstring& address)
{
    if (address.empty())
        return false;

    wchar_t firstChar = address[0];
    return firstChar == L'3';
}

bool CryptocurrencyValidator::CheckLitecoinLegacyPrefix(const std::wstring& address)
{
    if (address.empty())
        return false;

    wchar_t firstChar = address[0];
    return firstChar == L'L' || firstChar == L'M' || firstChar == L'3';
}

bool CryptocurrencyValidator::CheckTronPrefix(const std::wstring& address)
{
    if (address.empty())
        return false;

    return address[0] == L'T';
}

bool CryptocurrencyValidator::CheckRipplePrefix(const std::wstring& address)
{
    if (address.empty())
        return false;

    return address[0] == L'r';
}

bool CryptocurrencyValidator::CheckDogecoinPrefix(const std::wstring& address)
{
    if (address.empty())
        return false;

    return address[0] == L'D';
}

bool CryptocurrencyValidator::CheckAddressLength(const std::wstring& address, size_t minLen, size_t maxLen)
{
    size_t len = address.length();
    return len >= minLen && len <= maxLen;
}

bool CryptocurrencyValidator::CheckCharacterSet(const std::wstring& address, const std::wstring& allowedChars)
{
    for (wchar_t ch : address)
    {
        if (allowedChars.find(ch) == std::wstring::npos)
            return false;
    }

    return true;
}

bool CryptocurrencyValidator::CheckHexFormat(const std::wstring& address)
{
    return CheckCharacterSet(address, kHexCharacters);
}

bool CryptocurrencyValidator::CheckBase58Format(const std::wstring& address)
{
    return CheckCharacterSet(address, kBase58Characters);
}

bool CryptocurrencyValidator::MatchesPattern(const std::wstring& address, const std::wregex& pattern)
{
    return std::regex_match(address, pattern);
}