#include "ClientArgs.hpp"

#include <arpa/inet.h>
#include <charconv>
#include <algorithm>
#include <cctype>

namespace tw::app {

namespace {

std::string_view trim(std::string_view str) {
    // Skip leading whitespace
    size_t start = 0;
    while(start < str.length() && std::isspace(static_cast<unsigned char>(str[start]))) {
        ++start;
    }

    // Skip trailing whitespace
    size_t end = str.length();
    while(end > start && std::isspace(static_cast<unsigned char>(str[end - 1]))) {
        --end;
    }

    return str.substr(start, end - start);
}

bool is_valid_ipv4(std::string_view ip_str) {
    // Use inet_pton to validate IPv4 format
    struct in_addr addr;
    return inet_pton(AF_INET, std::string(ip_str).c_str(), &addr) == 1;
}

tl::expected<int, std::string> parse_port(std::string_view port_str) {
    if(port_str.empty()) {
        return 8080; // Default port
    }

    int port = 0;
    const char* end = port_str.data() + port_str.length();
    auto result = std::from_chars(port_str.data(), end, port);

    // from_chars stops at the first character it cannot use, so a partially
    // numeric port like "80x" would otherwise be accepted as 80.
    if(result.ec != std::errc() || result.ptr != end) {
        return tl::make_unexpected("port must be numeric");
    }

    if(port < 1 || port > 65535) {
        return tl::make_unexpected("port must be between 1 and 65535");
    }

    return port;
}

} // anonymous namespace

tl::expected<net::Address, std::string> parse_address(std::string_view text) {
    // Trim whitespace
    text = trim(text);

    if(text.empty()) {
        return tl::make_unexpected("address cannot be empty");
    }

    // Find the colon to split host and port
    size_t colon_pos = text.rfind(':');

    std::string_view host;
    std::string_view port_str;

    if(colon_pos == std::string_view::npos) {
        // No colon found: treat entire string as port or host
        // If it's all digits, treat as port; otherwise as host (will fail validation)
        bool all_digits = !text.empty() && std::all_of(text.begin(), text.end(),
            [](unsigned char c) { return std::isdigit(c); });

        if(all_digits) {
            host = "127.0.0.1";
            port_str = text;
        } else {
            // Treat as host with no port
            host = text;
            port_str = "";
        }
    } else {
        host = text.substr(0, colon_pos);
        port_str = text.substr(colon_pos + 1);
    }

    // Validate host
    if(host.empty()) {
        return tl::make_unexpected("host cannot be empty");
    }

    if(!is_valid_ipv4(host)) {
        return tl::make_unexpected("not a valid IPv4 address");
    }

    // Parse port
    auto port_result = parse_port(port_str);
    if(!port_result) {
        return tl::make_unexpected(port_result.error());
    }

    int port = port_result.value();
    return net::Address(std::optional<std::string>(std::string(host)), port);
}

std::optional<std::string> server_arg(int argc, char** argv) {
    for(int i = 1; i < argc - 1; ++i) {
        if(std::string_view(argv[i]) == "--server") {
            return std::string(argv[i + 1]);
        }
    }
    return std::nullopt;
}

} // namespace tw::app
