//
//  TokenAuthInterceptor.hpp
//  eagle0
//
//  Token-based authentication for gRPC.
//  Validates Bearer tokens in the 'authorization' metadata header.
//

#ifndef EAGLE0_SHARDOK_SERVER_TOKEN_AUTH_INTERCEPTOR_HPP
#define EAGLE0_SHARDOK_SERVER_TOKEN_AUTH_INTERCEPTOR_HPP

#include <fstream>
#include <iostream>
#include <optional>
#include <sstream>
#include <string>

#include "src/main/cpp/net/eagle0/common/ProtobufWarningSuppression.hpp"
#pragma GCC diagnostic push
SUPPRESS_PROTOBUF_WARNINGS
#include <grpcpp/server_context.h>
#include <grpcpp/support/status.h>
#pragma GCC diagnostic pop

namespace shardok {

/**
 * Validates Bearer tokens in gRPC request metadata.
 *
 * Usage:
 *   TokenValidator validator(expectedToken);
 *   if (!validator.Validate(context)) {
 *       return grpc::Status(grpc::UNAUTHENTICATED, "Invalid token");
 *   }
 */
class TokenValidator {
public:
    /**
     * Create a validator with the expected token.
     * If expectedToken is empty, all requests are allowed (no auth required).
     */
    explicit TokenValidator(std::string expectedToken) : expectedToken_(std::move(expectedToken)) {}

    /**
     * Validate the authorization header in the server context.
     *
     * Expects: "authorization" metadata with value "Bearer <token>"
     *
     * @return true if valid (or no auth configured), false otherwise
     */
    [[nodiscard]] auto Validate(grpc::ServerContext* context) const -> bool {
        // If no auth token is configured, allow all requests
        if (expectedToken_.empty()) { return true; }

        const auto& metadata = context->client_metadata();
        auto it = metadata.find("authorization");
        if (it == metadata.end()) {
            std::cerr << "Auth failed: no authorization header" << '\n';
            return false;
        }

        std::string authHeader(it->second.begin(), it->second.end());
        std::string expectedHeader = "Bearer " + expectedToken_;

        if (authHeader != expectedHeader) {
            std::cerr << "Auth failed: invalid token" << '\n';
            return false;
        }

        return true;
    }

    /**
     * Validate and return appropriate Status.
     *
     * @return std::nullopt if valid, Status(UNAUTHENTICATED) if invalid
     */
    [[nodiscard]] auto ValidateOrStatus(grpc::ServerContext* context) const
            -> std::optional<grpc::Status> {
        if (Validate(context)) { return std::nullopt; }
        return grpc::Status(grpc::UNAUTHENTICATED, "Invalid or missing authentication token");
    }

    /**
     * Check if authentication is enabled.
     */
    [[nodiscard]] auto IsEnabled() const -> bool { return !expectedToken_.empty(); }

private:
    std::string expectedToken_;
};

/**
 * Read auth token from a file, stripping whitespace.
 * Returns empty string if file doesn't exist or is empty.
 */
[[nodiscard]] inline auto ReadAuthTokenFromFile(const std::string& filePath) -> std::string {
    if (filePath.empty()) { return ""; }

    std::ifstream file(filePath);
    if (!file.is_open()) {
        std::cerr << "Warning: Could not open auth token file: " << filePath << '\n';
        return "";
    }

    std::stringstream buffer;
    buffer << file.rdbuf();
    std::string token = buffer.str();

    // Trim whitespace
    auto start = token.find_first_not_of(" \t\n\r");
    if (start == std::string::npos) { return ""; }
    auto end = token.find_last_not_of(" \t\n\r");
    return token.substr(start, end - start + 1);
}

}  // namespace shardok

#endif  // EAGLE0_SHARDOK_SERVER_TOKEN_AUTH_INTERCEPTOR_HPP
