/**
 * @file WebModuleTests.cpp
 * @brief Integration tests for WebModule (10 TI)
 */

#include <catch2/catch_test_macros.hpp>
#include "mocks/MockIO.hpp"
#include "utils/TimeSimulator.hpp"
#include "utils/TestHelpers.hpp"

#include "modules/WebModule.h"
#include <grove/JsonDataNode.h>

using namespace celuna;
using namespace celuna::tests;

// ============================================================================
// Test Fixture
// ============================================================================

class WebTestFixture {
public:
    MockIO io;
    TimeSimulator time;
    WebModule module;

    void configure(const json& config = json::object()) {
        json fullConfig = {
            {"enabled", true},
            {"requestTimeoutMs", 5000},
            {"maxConcurrentRequests", 10}
        };
        fullConfig.merge_patch(config);

        grove::JsonDataNode configNode("config", fullConfig);
        module.setConfiguration(configNode, &io, nullptr);
    }

    void process() {
        grove::JsonDataNode input("input", time.createInput());
        module.process(input);
    }
};

// ============================================================================
// TI_WEB_001: Simple GET Request
// ============================================================================

TEST_CASE("TI_WEB_001_SimpleGetRequest", "[web][integration]") {
    WebTestFixture f;
    f.configure();

    // Send GET request (we use a public API that should work)
    f.io.injectMessage("web:request", {
        {"requestId", "test-001"},
        {"url", "https://api.github.com"},
        {"method", "GET"}
    });
    f.process();

    // Verify response published
    REQUIRE(f.io.wasPublished("web:response"));
    auto response = f.io.getLastPublished("web:response");
    REQUIRE(response["requestId"] == "test-001");
    REQUIRE(response.contains("success"));
    REQUIRE(response.contains("durationMs"));
}

// ============================================================================
// TI_WEB_002: POST Request with Body
// ============================================================================

TEST_CASE("TI_WEB_002_PostRequestWithBody", "[web][integration]") {
    WebTestFixture f;
    f.configure();

    // Send POST request
    f.io.injectMessage("web:request", {
        {"requestId", "test-002"},
        {"url", "https://httpbin.org/post"},
        {"method", "POST"},
        {"body", R"({"key": "value"})"}
    });
    f.process();

    // Verify response
    REQUIRE(f.io.wasPublished("web:response"));
    auto response = f.io.getLastPublished("web:response");
    REQUIRE(response["requestId"] == "test-002");
    REQUIRE(response.contains("success"));
}

// ============================================================================
// TI_WEB_003: Invalid URL Handling
// ============================================================================

TEST_CASE("TI_WEB_003_InvalidUrlHandling", "[web][integration]") {
    WebTestFixture f;
    f.configure();

    // Send request with invalid URL
    f.io.injectMessage("web:request", {
        {"requestId", "test-003"},
        {"url", ""},
        {"method", "GET"}
    });
    f.process();

    // Verify error response
    REQUIRE(f.io.wasPublished("web:response"));
    auto response = f.io.getLastPublished("web:response");
    REQUIRE(response["requestId"] == "test-003");
    REQUIRE(response["success"] == false);
    REQUIRE(response["errorCode"] == "INVALID_URL");
}

// ============================================================================
// TI_WEB_004: Timeout Handling
// ============================================================================

TEST_CASE("TI_WEB_004_TimeoutHandling", "[web][integration]") {
    WebTestFixture f;
    f.configure();

    // Send request with very short timeout to a slow endpoint
    f.io.injectMessage("web:request", {
        {"requestId", "test-004"},
        {"url", "https://httpbin.org/delay/10"},
        {"method", "GET"},
        {"timeoutMs", 100}
    });
    f.process();

    // Verify response (should timeout or fail)
    REQUIRE(f.io.wasPublished("web:response"));
    auto response = f.io.getLastPublished("web:response");
    REQUIRE(response["requestId"] == "test-004");
    // Either timeout or connection error
    REQUIRE(response.contains("success"));
}

// ============================================================================
// TI_WEB_005: Multiple Concurrent Requests
// ============================================================================

TEST_CASE("TI_WEB_005_MultipleConcurrentRequests", "[web][integration]") {
    WebTestFixture f;
    f.configure();

    // Send multiple requests
    f.io.injectMessage("web:request", {
        {"requestId", "test-005-a"},
        {"url", "https://api.github.com"},
        {"method", "GET"}
    });
    f.io.injectMessage("web:request", {
        {"requestId", "test-005-b"},
        {"url", "https://api.github.com"},
        {"method", "GET"}
    });
    f.io.injectMessage("web:request", {
        {"requestId", "test-005-c"},
        {"url", "https://api.github.com"},
        {"method", "GET"}
    });
    f.process();

    // Verify all responses
    REQUIRE(f.io.countPublished("web:response") >= 3);
    auto messages = f.io.getAllPublished("web:response");
    REQUIRE(messages.size() >= 3);
}

// ============================================================================
// TI_WEB_006: Request ID Tracking
// ============================================================================

TEST_CASE("TI_WEB_006_RequestIdTracking", "[web][integration]") {
    WebTestFixture f;
    f.configure();

    // Send request with specific ID
    std::string testId = "unique-request-id-12345";
    f.io.injectMessage("web:request", {
        {"requestId", testId},
        {"url", "https://api.github.com"},
        {"method", "GET"}
    });
    f.process();

    // Verify response has same ID
    REQUIRE(f.io.wasPublished("web:response"));
    auto response = f.io.getLastPublished("web:response");
    REQUIRE(response["requestId"] == testId);
}

// ============================================================================
// TI_WEB_007: Statistics Tracking
// ============================================================================

TEST_CASE("TI_WEB_007_StatisticsTracking", "[web][integration]") {
    WebTestFixture f;
    f.configure();

    // Initial health check
    auto health1 = f.module.getHealthStatus();
    int initialTotal = health1->getInt("totalRequests", 0);

    // Send successful request
    f.io.injectMessage("web:request", {
        {"requestId", "test-007"},
        {"url", "https://api.github.com"},
        {"method", "GET"}
    });
    f.process();

    // Check health status
    auto health2 = f.module.getHealthStatus();
    REQUIRE(health2->getInt("totalRequests", 0) == initialTotal + 1);
    REQUIRE(health2->getInt("successfulRequests", 0) >= 0);
}

// ============================================================================
// TI_WEB_008: State Serialization
// ============================================================================

TEST_CASE("TI_WEB_008_StateSerialization", "[web][integration]") {
    WebTestFixture f;
    f.configure();

    // Make some requests
    f.io.injectMessage("web:request", {
        {"requestId", "test-008"},
        {"url", "https://api.github.com"},
        {"method", "GET"}
    });
    f.process();

    // Get state
    auto state = f.module.getState();
    REQUIRE(state != nullptr);
    int totalRequests = state->getInt("totalRequests", 0);
    REQUIRE(totalRequests > 0);

    // Create new module and restore state
    WebModule newModule;
    grove::JsonDataNode emptyConfig("config");
    newModule.setConfiguration(emptyConfig, &f.io, nullptr);
    newModule.setState(*state);

    // Verify state restored
    auto restoredHealth = newModule.getHealthStatus();
    REQUIRE(restoredHealth->getInt("totalRequests", 0) == totalRequests);
}

// ============================================================================
// TI_WEB_009: Configuration Loading
// ============================================================================

TEST_CASE("TI_WEB_009_ConfigurationLoading", "[web][integration]") {
    WebTestFixture f;

    // Configure with custom values
    f.configure({
        {"enabled", false},
        {"requestTimeoutMs", 15000},
        {"maxConcurrentRequests", 5}
    });

    // Verify module respects enabled flag
    f.io.injectMessage("web:request", {
        {"requestId", "test-009"},
        {"url", "https://api.github.com"},
        {"method", "GET"}
    });
    f.process();

    // Should not process when disabled
    REQUIRE(f.io.countPublished("web:response") == 0);
}

// ============================================================================
// TI_WEB_010: Error Response Format
// ============================================================================

TEST_CASE("TI_WEB_010_ErrorResponseFormat", "[web][integration]") {
    WebTestFixture f;
    f.configure();

    // Send request with blocked URL (localhost)
    f.io.injectMessage("web:request", {
        {"requestId", "test-010"},
        {"url", "http://localhost:8080/test"},
        {"method", "GET"}
    });
    f.process();

    // Verify error response format
    REQUIRE(f.io.wasPublished("web:response"));
    auto response = f.io.getLastPublished("web:response");
    REQUIRE(response["requestId"] == "test-010");
    REQUIRE(response["success"] == false);
    REQUIRE(response.contains("error"));
    REQUIRE(response.contains("errorCode"));
    REQUIRE(response["errorCode"] == "BLOCKED_URL");
}
