#include "character_creation_worker.h"
#include "connection_guard.h"
#include <iostream>
#include <chrono>
#include <thread>
#include <random>

CharacterCreationWorker::CharacterCreationWorker(ConnectionPool &pool, MessageBroker &broker)
    : Worker(pool, broker, "CharacterCreationWorker"),
      gen(std::random_device{}()),
      dist(2, 3),
      deathCheckRunning(true),
      deathThread(&CharacterCreationWorker::monitorCharacterDeaths, this) {
}

CharacterCreationWorker::~CharacterCreationWorker() {
    deathCheckRunning.store(false);
    if (deathThread.joinable()) {
        deathThread.join();
    }
}

void CharacterCreationWorker::run() {
    while (runningWorker) {
        setCurrentStep("Check if previous day character was created");
        if (!isTodayCharacterCreated()) {
            setCurrentStep("Create characters for today");
            createCharactersForToday();
        }
        setCurrentStep("Sleep for 60 seconds");
        for (int i = 0; i < 60 && runningWorker; ++i) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            setCurrentStep("signalActivity()");
            signalActivity();
        }
        setCurrentStep("Loop done");
    }
}

bool CharacterCreationWorker::isTodayCharacterCreated() {
    try {
        setCurrentStep("Get Database Connection");
        ConnectionGuard connGuard(pool);
        auto &db = connGuard.get();
        setCurrentStep("Execute Query");
        auto results = db.query(QUERY_IS_PREVIOUS_DAY_CHARACTER_CREATED);
        return !results.empty();
    } catch (const std::exception &e) {
        std::cerr << "[CharacterCreationWorker] Fehler in isTodayCharacterCreated: " << e.what() << std::endl;
        return false;
    }
}

void CharacterCreationWorker::createCharactersForToday() {
    loadNames();
    if (first_name_cache.empty() || last_name_cache.empty()) {
        std::cerr << "[CharacterCreationWorker] Fehler: Namen konnten nicht geladen werden." << std::endl;
        return;
    }

    auto town_ids = getTownRegionIds();
    for (auto region_id : town_ids) {
        createCharactersForRegion(region_id);
    }
}

void CharacterCreationWorker::createCharactersForRegion(int region_id) {
    std::vector<int> nobility_stands = {1, 2, 3};
    std::vector<std::string> genders = {"male", "female"};
    for (auto nobility : nobility_stands) {
        for (const auto &gender : genders) {
            int num_chars = dist(gen);
            for (int i = 0; i < num_chars; ++i) {
                createCharacter(region_id, gender, nobility);
            }
        }
    }
}

void CharacterCreationWorker::createCharacter(int region_id, const std::string &gender, int title_of_nobility) {
    int first_name_id = getRandomFromSet(first_name_cache[gender]);
    if (first_name_id == -1) {
        std::cerr << "Fehler: Kein passender Vorname gefunden." << std::endl;
        return;
    }
    int last_name_id = getRandomFromSet(last_name_cache);
    if (last_name_id == -1) {
        std::cerr << "Fehler: Kein passender Nachname gefunden." << std::endl;
        return;
    }
    try {
        ConnectionGuard connGuard(pool);
        auto &db = connGuard.get();

        db.prepare("insert_character", QUERY_INSERT_CHARACTER);
        db.execute("insert_character", {std::to_string(region_id),
                                        std::to_string(first_name_id),
                                        std::to_string(last_name_id),
                                        gender,
                                        std::to_string(title_of_nobility)});
    } catch (const std::exception &e) {
        std::cerr << "[CharacterCreationWorker] Fehler in createCharacter: " << e.what() << std::endl;
    }
}

void CharacterCreationWorker::monitorCharacterDeaths() {
    while (deathCheckRunning) {
        try {
            ConnectionGuard connGuard(pool);
            auto &db = connGuard.get();

            auto results = db.query(QUERY_GET_ELIGIBLE_NPC_FOR_DEATH);
            for (const auto &row : results) {
                int characterId = std::stoi(row.at("id"));
                int age = std::stoi(row.at("age"));
                if (calculateDeathProbability(age)) {
                    handleCharacterDeath(characterId);
                }
            }
        } catch (const std::exception &e) {
            std::cerr << "[CharacterCreationWorker] Fehler beim Überprüfen von Todesfällen: " << e.what() << std::endl;
        }

        std::this_thread::sleep_for(std::chrono::hours(1));
    }
}

bool CharacterCreationWorker::calculateDeathProbability(int age) {
    if (age < 60) {
        return false;
    }

    double baseProbability = 0.01;
    double increasePerYear = 0.01;
    double deathProbability = baseProbability + (increasePerYear * (age - 60));

    std::uniform_real_distribution<double> deathDist(0.0, 1.0);
    return deathDist(gen) < deathProbability;
}

void CharacterCreationWorker::handleCharacterDeath(int characterId) {
    try {
        ConnectionGuard connGuard(pool);
        auto &db = connGuard.get();

        // 1) Director löschen und User benachrichtigen
        db.prepare("delete_director", QUERY_DELETE_DIRECTOR);
        auto dirResult = db.execute("delete_director", { std::to_string(characterId) });
        if (!dirResult.empty()) {
            int userId = std::stoi(dirResult[0].at("user_id"));
            notifyUser(userId, "director_death");
        }

        // 2) Relationships löschen und betroffene User benachrichtigen
        db.prepare("delete_relationship", QUERY_DELETE_RELATIONSHIP);
        auto relResult = db.execute("delete_relationship", { std::to_string(characterId) });
        for (auto &row : relResult) {
            int relatedUserId = std::stoi(row.at("related_user_id"));
            notifyUser(relatedUserId, "relationship_death");
        }

        // 3) Child-Relations löschen und Eltern benachrichtigen
        db.prepare("delete_child_relation", QUERY_DELETE_CHILD_RELATION);
        auto childResult = db.execute("delete_child_relation", { std::to_string(characterId) });
        for (auto &row : childResult) {
            int fatherUserId = std::stoi(row.at("father_user_id"));
            int motherUserId = std::stoi(row.at("mother_user_id"));
            notifyUser(fatherUserId, "child_death");
            notifyUser(motherUserId, "child_death");
        }

        // 4) Charakter als verstorben markieren
        markCharacterAsDeceased(characterId);

    } catch (const std::exception &e) {
        std::cerr << "[CharacterCreationWorker] Fehler beim Bearbeiten des Todes: "
                  << e.what() << std::endl;
    }
}

void CharacterCreationWorker::notifyUser(int userId, const std::string &eventType) {
    try {
        ConnectionGuard connGuard(pool);
        auto &db = connGuard.get();

        db.prepare("insert_notification", QUERY_INSERT_NOTIFICATION);
        db.execute("insert_notification", { std::to_string(userId) });

        // Sende falukantUpdateStatus nach dem Einfügen der Benachrichtigung
        nlohmann::json updateMessage = {
            {"event", "falukantUpdateStatus"},
            {"user_id", userId}
        };
        broker.publish(updateMessage.dump());

        // Sende auch die ursprüngliche Benachrichtigung
        nlohmann::json message = {
            {"event", eventType},
            {"user_id", userId}
        };
        broker.publish(message.dump());
    } catch (const std::exception &e) {
        std::cerr << "[CharacterCreationWorker] Fehler beim Senden der Benachrichtigung: "
                  << e.what() << std::endl;
    }
}

void CharacterCreationWorker::markCharacterAsDeceased(int characterId) {
    try {
        ConnectionGuard connGuard(pool);
        auto &db = connGuard.get();

        db.prepare("mark_character_deceased", QUERY_MARK_CHARACTER_DECEASED);
        db.execute("mark_character_deceased", {std::to_string(characterId)});
    } catch (const std::exception &e) {
        std::cerr << "[CharacterCreationWorker] Fehler beim Markieren des Charakters als verstorben: " << e.what() << std::endl;
    }
}

std::vector<int> CharacterCreationWorker::getTownRegionIds() {
    try {
        ConnectionGuard connGuard(pool);
        auto &db = connGuard.get();
        auto rows = db.query(QUERY_GET_TOWN_REGION_IDS);
        std::vector<int> ids;
        for (const auto &row : rows) {
            ids.push_back(std::stoi(row.at("id")));
        }
        return ids;
    } catch (const std::exception &e) {
        std::cerr << "[CharacterCreationWorker] Fehler in getTownRegionIds: " << e.what() << std::endl;
        return {};
    }
}

void CharacterCreationWorker::loadNames() {
    try {
        ConnectionGuard connGuard(pool);
        auto &db = connGuard.get();
        auto firstNameRows = db.query(QUERY_LOAD_FIRST_NAMES);
        for (const auto &row : firstNameRows) {
            first_name_cache[row.at("gender")].insert(std::stoi(row.at("id")));
        }
        auto lastNameRows = db.query(QUERY_LOAD_LAST_NAMES);
        for (const auto &row : lastNameRows) {
            last_name_cache.insert(std::stoi(row.at("id")));
        }
    } catch (const std::exception &e) {
        std::cerr << "[CharacterCreationWorker] Fehler in loadNames: " << e.what() << std::endl;
    }
}

int CharacterCreationWorker::getRandomFromSet(const std::unordered_set<int> &name_set) {
    if (name_set.empty()) {
        return -1;
    }
    auto it = name_set.begin();
    std::advance(it, std::uniform_int_distribution<int>(0, name_set.size() - 1)(gen));
    return *it;
}
