/**
 * @file AIModuleTests.cpp
 * @brief Integration tests for AIModule (10 TI)
 */

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

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

using namespace celuna;
using namespace celuna::tests;

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

class AITestFixture {
public:
    MockIO io;
    TimeSimulator time;
    AIModule module;

    void configure(const json& config = json::object()) {
        json fullConfig = {
            {"system_prompt", "Tu es un assistant personnel intelligent."},
            {"max_iterations", 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_AI_001: Query Sends LLM Request
// ============================================================================

TEST_CASE("TI_AI_001_QuerySendsLLMRequest", "[ai][integration]") {
    AITestFixture f;
    f.configure();

    // Send query
    f.io.injectMessage("ai:query", {{"query", "Quelle heure est-il?"}});
    f.process();

    // Verify LLM request published
    REQUIRE(f.io.wasPublished("llm:request"));
    auto msg = f.io.getLastPublished("llm:request");
    REQUIRE(msg["query"] == "Quelle heure est-il?");
}

// ============================================================================
// TI_AI_002: Voice Transcription Triggers Query
// ============================================================================

TEST_CASE("TI_AI_002_VoiceTranscriptionTriggersQuery", "[ai][integration]") {
    AITestFixture f;
    f.configure();

    // Send voice transcription
    f.io.injectMessage("voice:transcription", {
        {"text", "Aide-moi avec mon code"},
        {"confidence", 0.95}
    });
    f.process();

    // Verify LLM request
    REQUIRE(f.io.wasPublished("llm:request"));
    auto msg = f.io.getLastPublished("llm:request");
    REQUIRE(msg["query"] == "Aide-moi avec mon code");
}

// ============================================================================
// TI_AI_003: LLM Response Handled
// ============================================================================

TEST_CASE("TI_AI_003_LLMResponseHandled", "[ai][integration]") {
    AITestFixture f;
    f.configure();

    // Send query to set awaiting state
    f.io.injectMessage("ai:query", {{"query", "Test"}});
    f.process();
    REQUIRE(f.module.isIdle() == false);

    // Receive response
    f.io.injectMessage("llm:response", {
        {"text", "Voici la reponse"},
        {"tokens", 100},
        {"conversationId", "default"}
    });
    f.process();

    // Verify no longer awaiting
    REQUIRE(f.module.isIdle() == true);
}

// ============================================================================
// TI_AI_004: LLM Error Handled
// ============================================================================

TEST_CASE("TI_AI_004_LLMErrorHandled", "[ai][integration]") {
    AITestFixture f;
    f.configure();

    // Send query
    f.io.injectMessage("ai:query", {{"query", "Test"}});
    f.process();
    REQUIRE(f.module.isIdle() == false);

    // Receive error
    f.io.injectMessage("llm:error", {
        {"message", "API rate limit exceeded"},
        {"conversationId", "default"}
    });
    f.process();

    // Should no longer be awaiting
    REQUIRE(f.module.isIdle() == true);
}

// ============================================================================
// TI_AI_005: Hyperfocus Alert Generates Suggestion
// ============================================================================

TEST_CASE("TI_AI_005_HyperfocusAlertGeneratesSuggestion", "[ai][integration]") {
    AITestFixture f;
    f.configure();

    // Receive hyperfocus alert
    f.io.injectMessage("scheduler:hyperfocus_alert", {
        {"sessionMinutes", 130},
        {"task", "coding"}
    });
    f.process();

    // Verify LLM request published
    REQUIRE(f.io.wasPublished("llm:request"));
    auto req = f.io.getLastPublished("llm:request");
    std::string convId = req["conversationId"];

    // Simulate LLM response
    f.io.injectMessage("llm:response", {
        {"text", "Time to take a break!"},
        {"conversationId", convId}
    });
    f.process();

    // Verify suggestion published
    REQUIRE(f.io.wasPublished("ai:suggestion"));
    auto msg = f.io.getLastPublished("ai:suggestion");
    REQUIRE(msg.contains("message"));
}

// ============================================================================
// TI_AI_006: Break Reminder Generates Suggestion
// ============================================================================

TEST_CASE("TI_AI_006_BreakReminderGeneratesSuggestion", "[ai][integration]") {
    AITestFixture f;
    f.configure();

    // Receive break reminder
    f.io.injectMessage("scheduler:break_reminder", {
        {"workMinutes", 45}
    });
    f.process();

    // Verify LLM request published
    REQUIRE(f.io.wasPublished("llm:request"));
    auto req = f.io.getLastPublished("llm:request");
    std::string convId = req["conversationId"];

    // Simulate LLM response
    f.io.injectMessage("llm:response", {
        {"text", "Take a short break now!"},
        {"conversationId", convId}
    });
    f.process();

    // Verify suggestion
    REQUIRE(f.io.wasPublished("ai:suggestion"));
}

// ============================================================================
// TI_AI_007: System Prompt In Request
// ============================================================================

TEST_CASE("TI_AI_007_SystemPromptInRequest", "[ai][integration]") {
    AITestFixture f;
    f.configure({{"system_prompt", "Custom prompt here"}});

    f.io.injectMessage("ai:query", {{"query", "Test"}});
    f.process();

    REQUIRE(f.io.wasPublished("llm:request"));
    auto msg = f.io.getLastPublished("llm:request");
    REQUIRE(msg["systemPrompt"] == "Custom prompt here");
}

// ============================================================================
// TI_AI_008: Conversation ID Tracking
// ============================================================================

TEST_CASE("TI_AI_008_ConversationIdTracking", "[ai][integration]") {
    AITestFixture f;
    f.configure();

    // First query
    f.io.injectMessage("ai:query", {{"query", "Question 1"}});
    f.process();

    auto msg1 = f.io.getLastPublished("llm:request");
    std::string convId = msg1["conversationId"];
    REQUIRE(!convId.empty());

    // Simulate response
    f.io.injectMessage("llm:response", {{"text", "Response"}, {"conversationId", convId}});
    f.process();
    f.io.clearPublished();

    // Second query should use same conversation
    f.io.injectMessage("ai:query", {{"query", "Question 2"}});
    f.process();

    auto msg2 = f.io.getLastPublished("llm:request");
    REQUIRE(msg2["conversationId"] == convId);
}

// ============================================================================
// TI_AI_009: Token Counting Accumulates
// ============================================================================

TEST_CASE("TI_AI_009_TokenCountingAccumulates", "[ai][integration]") {
    AITestFixture f;
    f.configure();

    // Query 1
    f.io.injectMessage("ai:query", {{"query", "Q1"}});
    f.process();
    f.io.injectMessage("llm:response", {{"text", "R1"}, {"tokens", 50}});
    f.process();

    // Query 2
    f.io.injectMessage("ai:query", {{"query", "Q2"}});
    f.process();
    f.io.injectMessage("llm:response", {{"text", "R2"}, {"tokens", 75}});
    f.process();

    // Verify total
    auto state = f.module.getState();
    // TODO: Verify totalTokens == 125
    SUCCEED(); // Placeholder
}

// ============================================================================
// TI_AI_010: State Serialization
// ============================================================================

TEST_CASE("TI_AI_010_StateSerialization", "[ai][integration]") {
    AITestFixture f;
    f.configure();

    // Build state
    f.io.injectMessage("ai:query", {{"query", "Test"}});
    f.process();
    f.io.injectMessage("llm:response", {{"text", "Response"}, {"tokens", 100}});
    f.process();

    // Get state
    auto state = f.module.getState();
    REQUIRE(state != nullptr);

    // Restore
    AIModule module2;
    grove::JsonDataNode configNode2("config", json::object());
    module2.setConfiguration(configNode2, &f.io, nullptr);
    module2.setState(*state);

    auto state2 = module2.getState();
    REQUIRE(state2 != nullptr);
    SUCCEED(); // Placeholder
}
