/**
 * @file VoiceModuleTests.cpp
 * @brief Integration tests for VoiceModule (10 TI)
 */

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

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

using namespace celuna;
using namespace celuna::tests;

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

class VoiceTestFixture {
public:
    MockIO io;
    TimeSimulator time;
    VoiceModule module;

    void configure(const json& config = json::object()) {
        json fullConfig = {
            {"ttsEnabled", true},
            {"sttEnabled", true},
            {"language", "fr"}
        };
        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_VOICE_001: AI Response Triggers Speak
// ============================================================================

TEST_CASE("TI_VOICE_001_AIResponseTriggersSpeak", "[voice][integration]") {
    VoiceTestFixture f;
    f.configure();

    // Receive AI response
    f.io.injectMessage("ai:response", {
        {"text", "Voici la reponse a ta question"}
    });
    f.process();

    // Verify speak request
    REQUIRE(f.io.wasPublished("voice:speak"));
    auto msg = f.io.getLastPublished("voice:speak");
    REQUIRE(msg["text"] == "Voici la reponse a ta question");
}

// ============================================================================
// TI_VOICE_002: Suggestion Priority Speak
// ============================================================================

TEST_CASE("TI_VOICE_002_SuggestionPrioritySpeak", "[voice][integration]") {
    VoiceTestFixture f;
    f.configure();

    // Receive suggestion (should be priority)
    f.io.injectMessage("ai:suggestion", {
        {"message", "Tu devrais faire une pause"},
        {"duration", 5}
    });
    f.process();

    // Verify speak with priority
    REQUIRE(f.io.wasPublished("voice:speak"));
    auto msg = f.io.getLastPublished("voice:speak");
    REQUIRE(msg["priority"] == true);
}

// ============================================================================
// TI_VOICE_003: Speaking Started Updates State
// ============================================================================

TEST_CASE("TI_VOICE_003_SpeakingStartedUpdatesState", "[voice][integration]") {
    VoiceTestFixture f;
    f.configure();

    // Initially idle
    REQUIRE(f.module.isIdle() == true);

    // Receive speaking started
    f.io.injectMessage("voice:speaking_started", {{"text", "Hello"}});
    f.process();

    // Should be speaking
    REQUIRE(f.module.isIdle() == false);
}

// ============================================================================
// TI_VOICE_004: Speaking Ended Updates State
// ============================================================================

TEST_CASE("TI_VOICE_004_SpeakingEndedUpdatesState", "[voice][integration]") {
    VoiceTestFixture f;
    f.configure();

    // Start speaking
    f.io.injectMessage("voice:speaking_started", {{"text", "Hello"}});
    f.process();
    REQUIRE(f.module.isIdle() == false);

    // End speaking
    f.io.injectMessage("voice:speaking_ended", {});
    f.process();

    // Should be idle
    REQUIRE(f.module.isIdle() == true);
}

// ============================================================================
// TI_VOICE_005: IsIdle Reflects Speaking
// ============================================================================

TEST_CASE("TI_VOICE_005_IsIdleReflectsSpeaking", "[voice][integration]") {
    VoiceTestFixture f;
    f.configure();

    // Not speaking = idle
    REQUIRE(f.module.isIdle() == true);

    // Start speaking
    f.io.injectMessage("voice:speaking_started", {});
    f.process();
    REQUIRE(f.module.isIdle() == false);

    // Stop speaking
    f.io.injectMessage("voice:speaking_ended", {});
    f.process();
    REQUIRE(f.module.isIdle() == true);
}

// ============================================================================
// TI_VOICE_006: Transcription Forwarded (No Re-publish)
// ============================================================================

TEST_CASE("TI_VOICE_006_TranscriptionForwarded", "[voice][integration]") {
    VoiceTestFixture f;
    f.configure();

    // Receive transcription
    f.io.injectMessage("voice:transcription", {
        {"text", "Test transcription"},
        {"confidence", 0.9}
    });
    f.process();

    // VoiceModule should NOT re-publish transcription
    // It just updates internal state
    REQUIRE(f.io.countPublished("voice:transcription") == 0);
}

// ============================================================================
// TI_VOICE_007: Total Spoken Incremented
// ============================================================================

TEST_CASE("TI_VOICE_007_TotalSpokenIncremented", "[voice][integration]") {
    VoiceTestFixture f;
    f.configure();

    // Complete one speak cycle
    f.io.injectMessage("voice:speaking_started", {});
    f.process();
    f.io.injectMessage("voice:speaking_ended", {});
    f.process();

    // Complete another
    f.io.injectMessage("voice:speaking_started", {});
    f.process();
    f.io.injectMessage("voice:speaking_ended", {});
    f.process();

    // Verify counter
    auto state = f.module.getState();
    // TODO: Verify totalSpoken == 2
    SUCCEED(); // Placeholder
}

// ============================================================================
// TI_VOICE_008: TTS Disabled Config
// ============================================================================

TEST_CASE("TI_VOICE_008_TTSDisabledConfig", "[voice][integration]") {
    VoiceTestFixture f;
    f.configure({{"ttsEnabled", false}});

    // Try to trigger speak
    f.io.injectMessage("ai:response", {{"text", "Should not speak"}});
    f.process();

    // Should NOT publish speak request
    REQUIRE(f.io.wasPublished("voice:speak") == false);
}

// ============================================================================
// TI_VOICE_009: Tool Command Speak
// ============================================================================

TEST_CASE("TI_VOICE_009_ToolCommandSpeak", "[voice][integration]") {
    VoiceTestFixture f;
    f.configure();

    // Send speak command via tool
    f.io.injectMessage("voice:command", {
        {"action", "speak"},
        {"text", "Hello from tool"}
    });
    f.process();

    // Verify speak published
    REQUIRE(f.io.wasPublished("voice:speak"));
    auto msg = f.io.getLastPublished("voice:speak");
    REQUIRE(msg["text"] == "Hello from tool");
}

// ============================================================================
// TI_VOICE_010: State Serialization
// ============================================================================

TEST_CASE("TI_VOICE_010_StateSerialization", "[voice][integration]") {
    VoiceTestFixture f;
    f.configure();

    // Build state
    f.io.injectMessage("voice:speaking_started", {});
    f.process();
    f.io.injectMessage("voice:speaking_ended", {});
    f.process();

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

    // Restore
    VoiceModule 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
}
