// File: politics_worker.cpp

#include "politics_worker.h"
#include <iostream>
#include <chrono>

PoliticsWorker::PoliticsWorker(ConnectionPool &pool, MessageBroker &broker)
    : Worker(pool, broker, "PoliticsWorker")
{
}

PoliticsWorker::~PoliticsWorker()
{
}

void PoliticsWorker::run() {
    auto lastExecutionDate = std::chrono::system_clock::time_point{};
    while (runningWorker) {
        signalActivity();
        auto now        = std::chrono::system_clock::now();
        auto todayFloor = std::chrono::floor<std::chrono::days>(now);
        auto targetTime = todayFloor + std::chrono::hours(3); // 03:00 Uhr
        if (now >= targetTime && lastExecutionDate < todayFloor) {
            signalActivity();
            performDailyPoliticsTask();
            lastExecutionDate = todayFloor;
        }
        for (int i = 0; i < 5 && runningWorker.load(); ++i) {
            signalActivity();
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
}

void PoliticsWorker::performDailyPoliticsTask() {
    try {
        // … (Schritte für Notifications und evaluatePoliticalPositions) …

        // 3) Elections anlegen und **je 2 × posts_to_fill** Kandidaten hinzufügen
        {
            setCurrentStep("Schedule Elections and Insert Candidates");

            // 3a) Neue Elections erzeugen (liefert jetzt auch posts_to_fill)
            auto elections = scheduleElections();

            if (!elections.empty()) {
                for (auto const & tpl : elections) {
                    int electionId    = std::get<0>(tpl);
                    int regionId      = std::get<1>(tpl);
                    int postsToFill   = std::get<2>(tpl);

                    ConnectionGuard connGuard(pool);
                    auto &db = connGuard.get();

                    db.prepare("INSERT_CANDIDATES", QUERY_INSERT_CANDIDATES);
                    // $1 = electionId, $2 = regionId, $3 = postsToFill
                    db.execute("INSERT_CANDIDATES", {
                        std::to_string(electionId),
                        std::to_string(regionId),
                        std::to_string(postsToFill)
                    });
                }
            }
        }

        // … nach scheduleElections() & Kandidaten …
        {
            setCurrentStep("Process Elections After 3 Days");
            auto newOffices = processElections();
            for (auto const &tup : newOffices) {
                notifyOfficeFilled({tup});
            }
        }

    } catch (std::exception const & e) {
        std::cerr << "[PoliticsWorker] Fehler bei performDailyPoliticsTask: " << e.what() << "\n";
    }
}

void PoliticsWorker::evaluatePoliticalPositions(
    std::unordered_map<int,int>& requiredPerRegion,
    std::unordered_map<int,int>& occupiedPerRegion
) {
    ConnectionGuard connGuard(pool);
    auto &db = connGuard.get();

    signalActivity();
    db.prepare("COUNT_OFFICES_PER_REGION", QUERY_COUNT_OFFICES_PER_REGION);
    signalActivity();
    const auto result = db.execute("COUNT_OFFICES_PER_REGION");
    signalActivity();

    for (const auto &row : result) {
        int regionId    = std::stoi(row.at("region_id"));
        int reqCount    = std::stoi(row.at("required_count"));
        int occCount    = std::stoi(row.at("occupied_count"));

        requiredPerRegion[regionId] = reqCount;
        occupiedPerRegion[regionId] = occCount;

        signalActivity();
    }
}

// politics_worker.cpp  (Auszug)

std::vector<std::tuple<int,int,int>> PoliticsWorker::scheduleElections() {
    ConnectionGuard connGuard(pool);
    auto &db = connGuard.get();
    signalActivity();
    db.prepare("SELECT_NEEDED_ELECTIONS", QUERY_SELECT_NEEDED_ELECTIONS);
    signalActivity();
    auto result = db.execute("SELECT_NEEDED_ELECTIONS");
    signalActivity();
    std::vector<std::tuple<int,int,int>> created;
    created.reserve(result.size());
    for (auto const & row : result) {
        int electionId  = std::stoi(row.at("election_id"));
        int regionId    = std::stoi(row.at("region_id"));
        int postsToFill = std::stoi(row.at("posts_to_fill"));
        created.emplace_back(electionId, regionId, postsToFill);
        signalActivity();
    }
    return created;
}

std::vector<std::tuple<int,int,int,int>> PoliticsWorker::processExpiredOfficesAndFill() {
    ConnectionGuard connGuard(pool);
    auto &db = connGuard.get();

    signalActivity();
    db.prepare("PROCESS_EXPIRED_AND_FILL", QUERY_PROCESS_EXPIRED_AND_FILL);
    signalActivity();
    const auto result = db.execute("PROCESS_EXPIRED_AND_FILL");
    signalActivity();

    std::vector<std::tuple<int,int,int,int>> created;
    for (const auto &row : result) {
        int officeId     = std::stoi(row.at("office_id"));
        int officeTypeId = std::stoi(row.at("office_type_id"));
        int characterId  = std::stoi(row.at("character_id"));
        int regionId     = std::stoi(row.at("region_id"));
        created.emplace_back(officeId, officeTypeId, characterId, regionId);
        signalActivity();
    }
    return created;
}

std::vector<int> PoliticsWorker::getUserIdsInCitiesOfRegions(const std::vector<int>& regionIds) {
    if (regionIds.empty()) {
        return {};
    }

    ConnectionGuard connGuard(pool);
    auto &db = connGuard.get();

    std::vector<int> userIds;
    for (int rid : regionIds) {
        signalActivity();
        db.prepare("GET_USERS_IN_CITIES", QUERY_USERS_IN_CITIES_OF_REGIONS);
        signalActivity();
        const auto rows = db.execute("GET_USERS_IN_CITIES", { std::to_string(rid) });
        signalActivity();

        for (const auto &row : rows) {
            int uid = std::stoi(row.at("user_id"));
            userIds.push_back(uid);
            signalActivity();
        }
    }
    return userIds;
}

void PoliticsWorker::notifyOfficeExpirations() {
    ConnectionGuard connGuard(pool);
    auto &db = connGuard.get();

    signalActivity();
    db.prepare("NOTIFY_OFFICE_EXPIRATION", QUERY_NOTIFY_OFFICE_EXPIRATION);
    signalActivity();
    db.execute("NOTIFY_OFFICE_EXPIRATION");
    signalActivity();
    
    // Sende falukantUpdateStatus an alle betroffenen Benutzer
    db.prepare("GET_USERS_WITH_EXPIRING_OFFICES", QUERY_GET_USERS_WITH_EXPIRING_OFFICES);
    const auto affectedUsers = db.execute("GET_USERS_WITH_EXPIRING_OFFICES");
    for (const auto &user : affectedUsers) {
        int userId = std::stoi(user.at("user_id"));
        nlohmann::json message = { { "event", "falukantUpdateStatus" } };
        sendMessageToFalukantUsers(userId, message);
    }
}

void PoliticsWorker::notifyElectionCreated(const std::vector<std::pair<int,int>>& elections) {
    ConnectionGuard connGuard(pool);
    auto &db = connGuard.get();

    db.prepare("NOTIFY_ELECTION_CREATED", QUERY_NOTIFY_ELECTION_CREATED);
    for (const auto &pr : elections) {
        signalActivity();
        db.execute("NOTIFY_ELECTION_CREATED", { std::to_string(pr.first) });
        signalActivity();
    }
    
    // Sende falukantUpdateStatus an alle betroffenen Benutzer
    db.prepare("GET_USERS_IN_REGIONS_WITH_ELECTIONS", QUERY_GET_USERS_IN_REGIONS_WITH_ELECTIONS);
    const auto affectedUsers = db.execute("GET_USERS_IN_REGIONS_WITH_ELECTIONS");
    for (const auto &user : affectedUsers) {
        int userId = std::stoi(user.at("user_id"));
        nlohmann::json message = { { "event", "falukantUpdateStatus" } };
        sendMessageToFalukantUsers(userId, message);
    }
}

void PoliticsWorker::notifyOfficeFilled(const std::vector<std::tuple<int,int,int,int>>& newOffices) {
    ConnectionGuard connGuard(pool);
    auto &db = connGuard.get();

    db.prepare("NOTIFY_OFFICE_FILLED", QUERY_NOTIFY_OFFICE_FILLED);
    for (const auto &tup : newOffices) {
        int characterId = std::get<2>(tup);
        signalActivity();
        db.execute("NOTIFY_OFFICE_FILLED", { std::to_string(characterId) });
        signalActivity();
    }
    
    // Sende falukantUpdateStatus an alle betroffenen Benutzer
    db.prepare("GET_USERS_WITH_FILLED_OFFICES", QUERY_GET_USERS_WITH_FILLED_OFFICES);
    const auto affectedUsers = db.execute("GET_USERS_WITH_FILLED_OFFICES");
    for (const auto &user : affectedUsers) {
        int userId = std::stoi(user.at("user_id"));
        nlohmann::json message = { { "event", "falukantUpdateStatus" } };
        sendMessageToFalukantUsers(userId, message);
    }
}

std::vector<std::tuple<int,int,int,int>> PoliticsWorker::processElections() {
    ConnectionGuard connGuard(pool);
    auto &db = connGuard.get();
    db.prepare("PROCESS_ELECTIONS", QUERY_PROCESS_ELECTIONS);
    auto result = db.execute("PROCESS_ELECTIONS", {});
    std::vector<std::tuple<int,int,int,int>> created;
    for (auto const &row : result) {
        int officeId     = std::stoi(row.at("office_id"));
        int officeTypeId = std::stoi(row.at("office_type_id"));
        int characterId  = std::stoi(row.at("character_id"));
        int regionId     = std::stoi(row.at("region_id"));
        created.emplace_back(officeId, officeTypeId, characterId, regionId);
    }
    return created;
}
