#include "MonitoringModule.h"
#include <grove/IIO.h>
#include <grove/JsonDataNode.h>
#include <algorithm>

namespace celuna {

MonitoringModule::MonitoringModule() {
    m_logger = spdlog::get("MonitoringModule");
    if (!m_logger) {
        m_logger = spdlog::stdout_color_mt("MonitoringModule");
    }
    m_config = std::make_unique<grove::JsonDataNode>("config");
}

void MonitoringModule::setConfiguration(const grove::IDataNode& configNode,
                                        grove::IIO* io,
                                        grove::ITaskScheduler* scheduler) {
    m_io = io;
    m_config = std::make_unique<grove::JsonDataNode>("config");

    m_enabled = configNode.getBool("enabled", true);

    // Load productive apps list
    m_productiveApps.clear();
    auto* prodNode = const_cast<grove::IDataNode&>(configNode).getChildReadOnly("productive_apps");
    if (prodNode) {
        for (const auto& name : prodNode->getChildNames()) {
            m_productiveApps.insert(prodNode->getString(name, ""));
        }
    }
    // Default productive apps
    if (m_productiveApps.empty()) {
        m_productiveApps = {"Code", "code", "CLion", "clion", "Visual Studio",
                           "devenv", "rider", "idea", "pycharm"};
    }

    // Load distracting apps list
    m_distractingApps.clear();
    auto* distNode = const_cast<grove::IDataNode&>(configNode).getChildReadOnly("distracting_apps");
    if (distNode) {
        for (const auto& name : distNode->getChildNames()) {
            m_distractingApps.insert(distNode->getString(name, ""));
        }
    }
    if (m_distractingApps.empty()) {
        m_distractingApps = {"Discord", "discord", "Steam", "steam",
                            "firefox", "chrome", "YouTube"};
    }

    // Subscribe to platform events
    if (m_io) {
        grove::SubscriptionConfig subConfig;
        m_io->subscribe("platform:window_info", subConfig);
        m_io->subscribe("platform:window_changed", subConfig);
        m_io->subscribe("platform:idle_detected", subConfig);
        m_io->subscribe("platform:activity_resumed", subConfig);
        // Tool request handlers
        m_io->subscribe("monitoring:query", subConfig);
    }

    m_logger->info("MonitoringModule configure (v2 - sans infrastructure)");
}

const grove::IDataNode& MonitoringModule::getConfiguration() {
    return *m_config;
}

void MonitoringModule::process(const grove::IDataNode& input) {
    if (!m_enabled) return;
    processMessages();
}

void MonitoringModule::processMessages() {
    if (!m_io) return;

    while (m_io->hasMessages() > 0) {
        auto msg = m_io->pullMessage();

        if (msg.topic == "platform:window_changed" && msg.data) {
            handleWindowChanged(*msg.data);
        }
        else if (msg.topic == "platform:window_info" && msg.data) {
            handleWindowInfo(*msg.data);
        }
        else if (msg.topic == "platform:idle_detected" && msg.data) {
            handleIdleDetected(*msg.data);
        }
        else if (msg.topic == "platform:activity_resumed" && msg.data) {
            handleActivityResumed(*msg.data);
        }
        // Tool query handlers
        else if (msg.topic == "monitoring:query" && msg.data) {
            handleToolQuery(*msg.data);
        }
    }
}

void MonitoringModule::handleToolQuery(const grove::IDataNode& request) {
    std::string correlationId = request.getString("correlation_id", "");
    std::string action = request.getString("action", "");

    auto response = std::make_unique<grove::JsonDataNode>("response");
    response->setString("correlation_id", correlationId);

    if (action == "get_focus_stats") {
        std::string period = request.getString("period", "today");
        // For now, we only track "today" stats
        int productiveMinutes = m_totalProductiveSeconds / 60;
        int distractingMinutes = m_totalDistractingSeconds / 60;
        int totalMinutes = productiveMinutes + distractingMinutes;
        int focusScore = totalMinutes > 0 ? (productiveMinutes * 100 / totalMinutes) : 0;

        response->setInt("productive_minutes", productiveMinutes);
        response->setInt("distracting_minutes", distractingMinutes);
        response->setInt("total_minutes", totalMinutes);
        response->setInt("focus_score", focusScore);
        response->setString("period", period);
        response->setBool("is_idle", m_isIdle);
        response->setString("current_app", m_currentApp);
    }
    else if (action == "get_current_app") {
        response->setString("app_name", m_currentApp);
        response->setBool("is_productive", isProductiveApp(m_currentApp));
        response->setBool("is_distracting", isDistractingApp(m_currentApp));
        response->setBool("is_idle", m_isIdle);
    }
    else {
        response->setString("error", "unknown_action");
    }

    m_io->publish("monitoring:response", std::move(response));
}

void MonitoringModule::handleWindowChanged(const grove::IDataNode& data) {
    std::string oldApp = data.getString("oldApp", "");
    std::string newApp = data.getString("newApp", "");
    int duration = data.getInt("duration", 0);

    bool wasProductive = false;
    bool wasDistracting = false;

    if (!oldApp.empty() && duration > 0) {
        // Update duration tracking
        m_appDurations[oldApp] += duration;

        // Update productivity counters
        wasProductive = isProductiveApp(oldApp);
        wasDistracting = isDistractingApp(oldApp);

        if (wasProductive) {
            m_totalProductiveSeconds += duration;
        } else if (wasDistracting) {
            m_totalDistractingSeconds += duration;
        }

        m_logger->debug("App: {} -> {} ({}s, prod={})",
                       oldApp, newApp, duration, wasProductive);
    }

    // Always publish app change event (even for first app or zero duration)
    if (m_io && !newApp.empty()) {
        // Classify the new app
        bool isProductive = isProductiveApp(newApp);
        bool isDistracting = isDistractingApp(newApp);
        std::string classification = "neutral";
        if (isProductive) classification = "productive";
        else if (isDistracting) classification = "distracting";

        auto event = std::make_unique<grove::JsonDataNode>("changed");
        event->setString("appName", newApp);  // Primary field for current app
        event->setString("oldApp", oldApp);
        event->setString("newApp", newApp);
        event->setInt("duration", duration);
        event->setBool("wasProductive", wasProductive);
        event->setBool("wasDistracting", wasDistracting);
        event->setString("classification", classification);
        m_io->publish("monitoring:app_changed", std::move(event));
    }

    m_currentApp = newApp;
}

void MonitoringModule::handleWindowInfo(const grove::IDataNode& data) {
    std::string appName = data.getString("appName", "");
    if (!appName.empty() && appName != m_currentApp) {
        m_currentApp = appName;
    }
}

void MonitoringModule::handleIdleDetected(const grove::IDataNode& data) {
    m_isIdle = true;
    int idleSeconds = data.getInt("idleSeconds", 0);
    m_logger->info("User idle detected ({}s)", idleSeconds);

    if (m_io) {
        auto event = std::make_unique<grove::JsonDataNode>("idle");
        event->setInt("idleSeconds", idleSeconds);
        m_io->publish("monitoring:idle_detected", std::move(event));
    }
}

void MonitoringModule::handleActivityResumed(const grove::IDataNode& data) {
    m_isIdle = false;
    m_logger->info("User activity resumed");

    if (m_io) {
        auto event = std::make_unique<grove::JsonDataNode>("active");
        m_io->publish("monitoring:activity_resumed", std::move(event));
    }
}

bool MonitoringModule::isProductiveApp(const std::string& appName) const {
    // Check exact match
    if (m_productiveApps.count(appName)) return true;

    // Check if app name contains productive keyword
    std::string lowerApp = appName;
    std::transform(lowerApp.begin(), lowerApp.end(), lowerApp.begin(), ::tolower);

    for (const auto& prod : m_productiveApps) {
        std::string lowerProd = prod;
        std::transform(lowerProd.begin(), lowerProd.end(), lowerProd.begin(), ::tolower);
        if (lowerApp.find(lowerProd) != std::string::npos) return true;
    }
    return false;
}

bool MonitoringModule::isDistractingApp(const std::string& appName) const {
    if (m_distractingApps.count(appName)) return true;

    std::string lowerApp = appName;
    std::transform(lowerApp.begin(), lowerApp.end(), lowerApp.begin(), ::tolower);

    for (const auto& dist : m_distractingApps) {
        std::string lowerDist = dist;
        std::transform(lowerDist.begin(), lowerDist.end(), lowerDist.begin(), ::tolower);
        if (lowerApp.find(lowerDist) != std::string::npos) return true;
    }
    return false;
}

std::unique_ptr<grove::IDataNode> MonitoringModule::getHealthStatus() {
    auto status = std::make_unique<grove::JsonDataNode>("status");
    status->setString("status", m_enabled ? "running" : "disabled");
    status->setString("currentApp", m_currentApp);
    status->setBool("isIdle", m_isIdle);
    status->setInt("totalProductiveSeconds", m_totalProductiveSeconds);
    status->setInt("totalDistractingSeconds", m_totalDistractingSeconds);
    return status;
}

void MonitoringModule::shutdown() {
    m_logger->info("MonitoringModule arrete. Productif: {}s, Distrait: {}s",
                  m_totalProductiveSeconds, m_totalDistractingSeconds);
}

std::unique_ptr<grove::IDataNode> MonitoringModule::getState() {
    auto state = std::make_unique<grove::JsonDataNode>("state");
    state->setString("currentApp", m_currentApp);
    state->setBool("isIdle", m_isIdle);
    state->setInt("totalProductiveSeconds", m_totalProductiveSeconds);
    state->setInt("totalDistractingSeconds", m_totalDistractingSeconds);
    return state;
}

void MonitoringModule::setState(const grove::IDataNode& state) {
    m_currentApp = state.getString("currentApp", "");
    m_isIdle = state.getBool("isIdle", false);
    m_totalProductiveSeconds = state.getInt("totalProductiveSeconds", 0);
    m_totalDistractingSeconds = state.getInt("totalDistractingSeconds", 0);

    m_logger->info("Etat restore: app={}, productif={}s", m_currentApp, m_totalProductiveSeconds);
}

} // namespace celuna

#ifndef CELUNA_TEST_BUILD
extern "C" {

grove::IModule* createModule() {
    return new celuna::MonitoringModule();
}

void destroyModule(grove::IModule* module) {
    delete module;
}

}
#endif
