/**
 * @file SchedulerModuleTests.cpp
 * @brief Integration tests for SchedulerModule (10 TI)
 */

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

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

using namespace celuna;
using namespace celuna::tests;

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

class SchedulerTestFixture {
public:
    MockIO io;
    TimeSimulator time;
    SchedulerModule module;

    void configure(const json& config = json::object()) {
        json fullConfig = {
            {"hyperfocusThresholdMinutes", 120},
            {"breakReminderIntervalMinutes", 45},
            {"breakDurationMinutes", 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);
    }

    void processWithTime(float gameTime) {
        time.setTime(gameTime);
        grove::JsonDataNode input("input", time.createInput(0.1f));
        module.process(input);
    }
};

// ============================================================================
// TI_SCHEDULER_001: Start Task
// ============================================================================

TEST_CASE("TI_SCHEDULER_001_StartTask", "[scheduler][integration]") {
    SchedulerTestFixture f;
    f.configure();

    // Inject task switch message
    f.io.injectMessage("user:task_switch", {{"taskId", "task-1"}});

    // Process
    f.process();

    // Verify task_started was published
    REQUIRE(f.io.wasPublished("scheduler:task_started"));
    auto msg = f.io.getLastPublished("scheduler:task_started");
    REQUIRE(msg["taskId"] == "task-1");
    REQUIRE(msg.contains("taskName"));
}

// ============================================================================
// TI_SCHEDULER_002: Complete Task
// ============================================================================

TEST_CASE("TI_SCHEDULER_002_CompleteTask", "[scheduler][integration]") {
    SchedulerTestFixture f;
    f.configure();

    // Start a task at time 0
    f.io.injectMessage("user:task_switch", {{"taskId", "task-1"}});
    f.processWithTime(0.0f);
    f.io.clearPublished();

    // Advance time 30 minutes (1800 seconds)
    f.time.setTime(1800.0f);

    // Switch to another task (completes current task implicitly)
    f.io.injectMessage("user:task_switch", {{"taskId", "task-2"}});
    f.process();

    // Verify task_completed was published with duration
    REQUIRE(f.io.wasPublished("scheduler:task_completed"));
    auto msg = f.io.getLastPublished("scheduler:task_completed");
    REQUIRE(msg["taskId"] == "task-1");
    REQUIRE(msg.contains("duration"));
    // Duration should be around 30 minutes
    int duration = msg["duration"].get<int>();
    REQUIRE(duration >= 29);
    REQUIRE(duration <= 31);
}

// ============================================================================
// TI_SCHEDULER_003: Hyperfocus Detection
// ============================================================================

TEST_CASE("TI_SCHEDULER_003_HyperfocusDetection", "[scheduler][integration]") {
    SchedulerTestFixture f;
    f.configure({{"hyperfocusThresholdMinutes", 120}});

    // Start a task at time 0
    f.io.injectMessage("user:task_switch", {{"taskId", "task-1"}});
    f.processWithTime(0.0f);
    f.io.clearPublished();

    // Advance time past threshold (121 minutes = 7260 seconds)
    f.processWithTime(7260.0f);

    // Verify hyperfocus alert
    REQUIRE(f.io.wasPublished("scheduler:hyperfocus_alert"));
    auto msg = f.io.getLastPublished("scheduler:hyperfocus_alert");
    REQUIRE(msg["type"] == "hyperfocus");
    REQUIRE(msg["task"] == "task-1");
    REQUIRE(msg["duration_minutes"].get<int>() >= 120);
}

// ============================================================================
// TI_SCHEDULER_004: Hyperfocus Alert Only Once
// ============================================================================

TEST_CASE("TI_SCHEDULER_004_HyperfocusAlertOnce", "[scheduler][integration]") {
    SchedulerTestFixture f;
    f.configure({{"hyperfocusThresholdMinutes", 120}});

    // Start task
    f.io.injectMessage("user:task_switch", {{"taskId", "task-1"}});
    f.processWithTime(0.0f);

    // Trigger hyperfocus (121 min)
    f.processWithTime(7260.0f);

    // Count first alert
    size_t alertCount = f.io.countPublished("scheduler:hyperfocus_alert");
    REQUIRE(alertCount == 1);

    // Continue processing (130 min, 140 min)
    f.processWithTime(7800.0f);
    f.processWithTime(8400.0f);

    // Should still be only 1 alert
    REQUIRE(f.io.countPublished("scheduler:hyperfocus_alert") == 1);
}

// ============================================================================
// TI_SCHEDULER_005: Break Reminder
// ============================================================================

TEST_CASE("TI_SCHEDULER_005_BreakReminder", "[scheduler][integration]") {
    SchedulerTestFixture f;
    f.configure({{"breakReminderIntervalMinutes", 45}});

    // Process at time 0 (sets lastBreakTime)
    f.processWithTime(0.0f);
    f.io.clearPublished();

    // Advance past break reminder interval (46 minutes = 2760 seconds)
    f.processWithTime(2760.0f);

    // Verify break reminder
    REQUIRE(f.io.wasPublished("scheduler:break_reminder"));
    auto msg = f.io.getLastPublished("scheduler:break_reminder");
    REQUIRE(msg["type"] == "break");
    REQUIRE(msg.contains("break_duration"));
}

// ============================================================================
// TI_SCHEDULER_006: Idle Pauses Session
// ============================================================================

TEST_CASE("TI_SCHEDULER_006_IdlePausesSession", "[scheduler][integration]") {
    SchedulerTestFixture f;
    f.configure();

    // Start task
    f.io.injectMessage("user:task_switch", {{"taskId", "task-1"}});
    f.processWithTime(0.0f);

    // Go idle
    f.io.injectMessage("monitoring:idle_detected", {{"idleSeconds", 300}});
    f.processWithTime(60.0f);

    // Verify module received and processed the idle message
    // (Module logs "User idle" - we can verify via state)
    auto state = f.module.getState();
    REQUIRE(state != nullptr);
    // Task should still be tracked (idle doesn't clear it)
    REQUIRE(state->getString("currentTaskId", "") == "task-1");
}

// ============================================================================
// TI_SCHEDULER_007: Activity Resumes Session
// ============================================================================

TEST_CASE("TI_SCHEDULER_007_ActivityResumesSession", "[scheduler][integration]") {
    SchedulerTestFixture f;
    f.configure();

    // Start task, go idle, resume
    f.io.injectMessage("user:task_switch", {{"taskId", "task-1"}});
    f.processWithTime(0.0f);
    f.io.injectMessage("monitoring:idle_detected", {});
    f.processWithTime(60.0f);
    f.io.injectMessage("monitoring:activity_resumed", {});
    f.processWithTime(120.0f);

    // Verify session continues - task still active
    auto state = f.module.getState();
    REQUIRE(state != nullptr);
    REQUIRE(state->getString("currentTaskId", "") == "task-1");
}

// ============================================================================
// TI_SCHEDULER_008: Tool Query Get Current Task
// ============================================================================

TEST_CASE("TI_SCHEDULER_008_ToolQueryGetCurrentTask", "[scheduler][integration]") {
    SchedulerTestFixture f;
    f.configure();

    // Start a task
    f.io.injectMessage("user:task_switch", {{"taskId", "task-1"}});
    f.processWithTime(0.0f);
    f.io.clearPublished();

    // Query current task
    f.io.injectMessage("scheduler:query", {
        {"action", "get_current_task"},
        {"correlation_id", "test-123"}
    });
    f.processWithTime(60.0f);

    // Verify response
    REQUIRE(f.io.wasPublished("scheduler:response"));
    auto resp = f.io.getLastPublished("scheduler:response");
    REQUIRE(resp["correlation_id"] == "test-123");
    REQUIRE(resp["task_id"] == "task-1");
}

// ============================================================================
// TI_SCHEDULER_009: Tool Command Start Break
// ============================================================================

TEST_CASE("TI_SCHEDULER_009_ToolCommandStartBreak", "[scheduler][integration]") {
    SchedulerTestFixture f;
    f.configure();

    // Start task
    f.io.injectMessage("user:task_switch", {{"taskId", "task-1"}});
    f.processWithTime(0.0f);
    f.io.clearPublished();

    // Command to start break
    f.io.injectMessage("scheduler:command", {
        {"action", "start_break"},
        {"duration_minutes", 15},
        {"reason", "test break"}
    });
    f.processWithTime(60.0f);

    // Verify break started was published
    REQUIRE(f.io.wasPublished("scheduler:break_started"));
    auto msg = f.io.getLastPublished("scheduler:break_started");
    REQUIRE(msg["duration"] == 15);
    REQUIRE(msg["reason"] == "test break");

    // Verify response was also published
    REQUIRE(f.io.wasPublished("scheduler:response"));
    auto resp = f.io.getLastPublished("scheduler:response");
    REQUIRE(resp["success"] == true);
}

// ============================================================================
// TI_SCHEDULER_010: State Serialization
// ============================================================================

TEST_CASE("TI_SCHEDULER_010_StateSerialization", "[scheduler][integration]") {
    SchedulerTestFixture f;
    f.configure();

    // Setup some state
    f.io.injectMessage("user:task_switch", {{"taskId", "task-1"}});
    f.processWithTime(0.0f);
    f.processWithTime(1800.0f);  // 30 minutes

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

    // Verify state content
    REQUIRE(state->getString("currentTaskId", "") == "task-1");
    REQUIRE(state->getBool("hyperfocusAlertSent", true) == false);

    // Create new module and restore state
    SchedulerModule module2;
    MockIO io2;
    grove::JsonDataNode configNode("config", json::object());
    module2.setConfiguration(configNode, &io2, nullptr);
    module2.setState(*state);

    // Verify state was restored
    auto state2 = module2.getState();
    REQUIRE(state2 != nullptr);
    REQUIRE(state2->getString("currentTaskId", "") == "task-1");
    REQUIRE(state2->getBool("hyperfocusAlertSent", true) == false);
}
