// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#include "payment_uri.h"
#include "address_validation.h"

#include <sstream>
#include <iomanip>
#include <algorithm>
#include <cctype>
#include <cstdio>

namespace dragonx {
namespace util {

std::string urlEncode(const std::string& value)
{
    std::string out;
    out.reserve(value.size());
    for (unsigned char c : value) {
        if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
            out += static_cast<char>(c);
        } else if (c == ' ') {
            out += "%20";
        } else {
            char hex[4];
            std::snprintf(hex, sizeof(hex), "%%%02X", c);
            out += hex;
        }
    }
    return out;
}

std::string buildPaymentUri(const std::string& address, double amount,
                            const std::string& label, const std::string& memo)
{
    if (address.empty()) return "";

    std::ostringstream uri;
    uri << "drgx:" << address;

    bool hasParams = false;
    auto addParam = [&](const char* key, const std::string& value) {
        if (value.empty()) return;
        uri << (hasParams ? "&" : "?") << key << "=" << value;
        hasParams = true;
    };

    if (amount > 0.0) {
        std::ostringstream amt;
        amt << std::fixed << std::setprecision(8) << amount;
        addParam("amount", amt.str());
    }
    addParam("label", urlEncode(label));
    addParam("memo", urlEncode(memo));

    return uri.str();
}

std::string urlDecode(const std::string& encoded)
{
    std::string result;
    result.reserve(encoded.size());
    
    for (size_t i = 0; i < encoded.size(); i++) {
        if (encoded[i] == '%' && i + 2 < encoded.size()) {
            // Decode hex pair
            char hex[3] = { encoded[i + 1], encoded[i + 2], '\0' };
            char* end;
            long val = strtol(hex, &end, 16);
            if (end == hex + 2) {
                result += static_cast<char>(val);
                i += 2;
                continue;
            }
        } else if (encoded[i] == '+') {
            result += ' ';
            continue;
        }
        result += encoded[i];
    }
    
    return result;
}

bool isPaymentURI(const std::string& str)
{
    // Check for drgx: or hush: prefix (case insensitive)
    std::string lower = str;
    std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
    return lower.substr(0, 5) == "drgx:" || lower.substr(0, 5) == "hush:";
}

PaymentURI parsePaymentURI(const std::string& uri)
{
    PaymentURI result;
    result.valid = false;
    
    // Check prefix
    if (!isPaymentURI(uri)) {
        result.error = "Invalid URI scheme. Expected drgx: or hush:";
        return result;
    }
    
    // Skip the scheme prefix (drgx: or hush:)
    size_t schemeEnd = uri.find(':');
    if (schemeEnd == std::string::npos) {
        result.error = "Invalid URI format";
        return result;
    }
    
    std::string remainder = uri.substr(schemeEnd + 1);
    
    // Handle double-slash format (drgx://address)
    if (remainder.size() >= 2 && remainder[0] == '/' && remainder[1] == '/') {
        remainder = remainder.substr(2);
    }
    
    // Split address and query string
    size_t queryStart = remainder.find('?');
    if (queryStart == std::string::npos) {
        // No query parameters, just the address
        result.address = remainder;
    } else {
        result.address = remainder.substr(0, queryStart);
        std::string queryString = remainder.substr(queryStart + 1);
        
        // Parse query parameters
        std::istringstream queryStream(queryString);
        std::string param;
        
        while (std::getline(queryStream, param, '&')) {
            size_t eqPos = param.find('=');
            if (eqPos == std::string::npos) continue;
            
            std::string key = param.substr(0, eqPos);
            std::string value = urlDecode(param.substr(eqPos + 1));
            
            // Convert key to lowercase for case-insensitive matching
            std::transform(key.begin(), key.end(), key.begin(), ::tolower);
            
            if (key == "amount" || key == "amt") {
                try {
                    result.amount = std::stod(value);
                } catch (...) {
                    // Invalid amount, keep as 0
                }
            } else if (key == "label") {
                result.label = value;
            } else if (key == "memo") {
                result.memo = value;
            } else if (key == "message" || key == "msg") {
                result.message = value;
            }
        }
    }
    
    // Validate address
    if (result.address.empty()) {
        result.error = "No address specified in URI";
        return result;
    }
    
    // Address format check via the shared, structure-based recognizers (checksum + real DragonX
    // address types). This accepts shielded ("zs…"), P2PKH ("R…") and P2SH/multisig ("b…") forms —
    // the old prefix/length heuristic rejected P2SH and hardcoded a 't' prefix DragonX never emits.
    const bool validFormat = isShieldedAddress(result.address) ||
                             isTransparentAddress(result.address);

    if (!validFormat) {
        result.error = "Invalid address format";
        return result;
    }
    
    // Validate amount if present
    if (result.amount < 0) {
        result.error = "Invalid negative amount";
        return result;
    }
    
    result.valid = true;
    return result;
}

} // namespace util
} // namespace dragonx
