/**
 * @file StorageModuleTests.cpp
 * @brief Integration tests for StorageModule (10 TI)
 */

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

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

using namespace celuna;
using namespace celuna::tests;

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

class StorageTestFixture {
public:
    MockIO io;
    TimeSimulator time;
    StorageModule module;

    void configure(const json& config = json::object()) {
        json fullConfig = json::object();
        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_STORAGE_001: Task Completed Saves Session
// ============================================================================

TEST_CASE("TI_STORAGE_001_TaskCompletedSavesSession", "[storage][integration]") {
    StorageTestFixture f;
    f.configure();

    // Receive task completed
    f.io.injectMessage("scheduler:task_completed", {
        {"taskId", "task-1"},
        {"taskName", "Coding session"},
        {"durationMinutes", 45},
        {"hyperfocus", false}
    });
    f.process();

    // Verify save_session published
    REQUIRE(f.io.wasPublished("storage:save_session"));
    auto msg = f.io.getLastPublished("storage:save_session");
    REQUIRE(msg["taskName"] == "Coding session");
    REQUIRE(msg["durationMinutes"] == 45);
}

// ============================================================================
// TI_STORAGE_002: App Changed Saves Usage
// ============================================================================

TEST_CASE("TI_STORAGE_002_AppChangedSavesUsage", "[storage][integration]") {
    StorageTestFixture f;
    f.configure();

    // Receive app changed with duration
    f.io.injectMessage("monitoring:app_changed", {
        {"appName", "Code"},
        {"oldApp", "Discord"},
        {"duration", 120},
        {"classification", "productive"}
    });
    f.process();

    // Verify save_app_usage published
    REQUIRE(f.io.wasPublished("storage:save_app_usage"));
    auto msg = f.io.getLastPublished("storage:save_app_usage");
    REQUIRE(msg["appName"] == "Discord");  // Old app that ended
    REQUIRE(msg["durationSeconds"] == 120);
}

// ============================================================================
// TI_STORAGE_003: Session Saved Updates Last ID
// ============================================================================

TEST_CASE("TI_STORAGE_003_SessionSavedUpdatesLastId", "[storage][integration]") {
    StorageTestFixture f;
    f.configure();

    // Receive session saved confirmation
    f.io.injectMessage("storage:session_saved", {
        {"sessionId", 42}
    });
    f.process();

    // Verify state updated
    auto state = f.module.getState();
    // TODO: Verify lastSessionId == 42
    SUCCEED(); // Placeholder
}

// ============================================================================
// TI_STORAGE_004: Storage Error Handled
// ============================================================================

TEST_CASE("TI_STORAGE_004_StorageErrorHandled", "[storage][integration]") {
    StorageTestFixture f;
    f.configure();

    // Receive storage error
    f.io.injectMessage("storage:error", {
        {"message", "Database locked"}
    });

    // Should not throw
    REQUIRE_NOTHROW(f.process());
}

// ============================================================================
// TI_STORAGE_005: Pending Saves Tracking
// ============================================================================

TEST_CASE("TI_STORAGE_005_PendingSavesTracking", "[storage][integration]") {
    StorageTestFixture f;
    f.configure();

    // Trigger save
    f.io.injectMessage("scheduler:task_completed", {
        {"taskId", "t1"},
        {"taskName", "Task"},
        {"durationMinutes", 10}
    });
    f.process();

    // Verify pending incremented
    auto state = f.module.getState();
    // TODO: Verify pendingSaves == 1
    SUCCEED(); // Placeholder
}

// ============================================================================
// TI_STORAGE_006: Total Saved Tracking
// ============================================================================

TEST_CASE("TI_STORAGE_006_TotalSavedTracking", "[storage][integration]") {
    StorageTestFixture f;
    f.configure();

    // Save and confirm multiple times
    for (int i = 0; i < 3; i++) {
        f.io.injectMessage("scheduler:task_completed", {
            {"taskId", "t" + std::to_string(i)},
            {"taskName", "Task"},
            {"durationMinutes", 10}
        });
        f.process();
        f.io.injectMessage("storage:session_saved", {{"sessionId", i}});
        f.process();
    }

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

// ============================================================================
// TI_STORAGE_007: Tool Query Notes
// ============================================================================

TEST_CASE("TI_STORAGE_007_ToolQueryNotes", "[storage][integration]") {
    StorageTestFixture f;
    f.configure();

    // Add a note first
    f.io.injectMessage("storage:command", {
        {"action", "save_note"},
        {"content", "Test note"},
        {"tags", json::array({"test", "important"})}
    });
    f.process();
    f.io.clearPublished();

    // Query notes
    f.io.injectMessage("storage:query", {
        {"action", "query_notes"},
        {"correlation_id", "query-1"}
    });
    f.process();

    // Verify response
    REQUIRE(f.io.wasPublished("storage:response"));
    auto resp = f.io.getLastPublished("storage:response");
    REQUIRE(resp["correlation_id"] == "query-1");
}

// ============================================================================
// TI_STORAGE_008: Tool Command Save Note
// ============================================================================

TEST_CASE("TI_STORAGE_008_ToolCommandSaveNote", "[storage][integration]") {
    StorageTestFixture f;
    f.configure();

    // Save note
    f.io.injectMessage("storage:command", {
        {"action", "save_note"},
        {"content", "Remember to check logs"},
        {"tags", json::array({"reminder"})}
    });
    f.process();

    // Verify note added to state
    auto state = f.module.getState();
    // TODO: Verify notes contains the new note
    SUCCEED(); // Placeholder
}

// ============================================================================
// TI_STORAGE_009: Note Tags Filtering
// ============================================================================

TEST_CASE("TI_STORAGE_009_NoteTagsFiltering", "[storage][integration]") {
    StorageTestFixture f;
    f.configure();

    // Add notes with different tags
    f.io.injectMessage("storage:command", {
        {"action", "save_note"},
        {"content", "Work note"},
        {"tags", json::array({"work"})}
    });
    f.process();
    f.io.injectMessage("storage:command", {
        {"action", "save_note"},
        {"content", "Personal note"},
        {"tags", json::array({"personal"})}
    });
    f.process();
    f.io.clearPublished();

    // Query with tag filter
    f.io.injectMessage("storage:query", {
        {"action", "query_notes"},
        {"tags", json::array({"work"})},
        {"correlation_id", "filter-1"}
    });
    f.process();

    // Verify filtered response
    REQUIRE(f.io.wasPublished("storage:response"));
    auto resp = f.io.getLastPublished("storage:response");
    // TODO: Verify only work notes returned
    SUCCEED(); // Placeholder
}

// ============================================================================
// TI_STORAGE_010: State Serialization
// ============================================================================

TEST_CASE("TI_STORAGE_010_StateSerialization", "[storage][integration]") {
    StorageTestFixture f;
    f.configure();

    // Build state with notes
    f.io.injectMessage("storage:command", {
        {"action", "save_note"},
        {"content", "Test note for serialization"},
        {"tags", json::array({"test"})}
    });
    f.process();

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

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