#ifndef VALUERECALCULATIONWORKER_H
#define VALUERECALCULATIONWORKER_H

#include "worker.h"
#include <unordered_map>
#include <functional>
#include <chrono>

class ValueRecalculationWorker : public Worker {
public:
    ValueRecalculationWorker(ConnectionPool &pool, MessageBroker &broker);
    ~ValueRecalculationWorker() override;

protected:
    void run() override;

private:
    struct Activity {
        std::chrono::system_clock::time_point lastRun;
        std::function<void()> callMethod;
        std::chrono::system_clock::duration scheduledTime;

        Activity(std::chrono::system_clock::time_point lr, std::function<void()> cm, std::chrono::system_clock::duration st)
            : lastRun(lr), callMethod(std::move(cm)), scheduledTime(st) {}
    };

    std::unordered_map<std::string, Activity> activities;

    void calculateProductKnowledge();
    void calculateRegionalSellPrice();
    void calculateMarriages();
    void calculateStudying();
    void calculateStudyingSelf(Database::FieldMap entry);
    void caclulateStudyingForAssociatedCharacter(Database::FieldMap entry);
    void calculateStudyingCharacter(int characterId, bool all, int productId, int falukantUserId);

    bool shouldRunToday(const Activity& activity);
    std::chrono::system_clock::time_point getNextScheduledTime(std::chrono::system_clock::duration scheduledDuration);

    static constexpr const char *QUERY_UPDATE_PRODUCT_KNOWLEDGE_USER = R"(
        UPDATE falukant_data.knowledge k
        SET knowledge = LEAST(100, k.knowledge + 1)
        FROM falukant_data."character" c
        JOIN falukant_log.production p
          ON DATE(p.production_timestamp) = CURRENT_DATE - INTERVAL '1 day'
        WHERE c.id = k.character_id
          AND c.user_id = 18
          AND k.product_id = 10
    )";

    static constexpr const char *QUERY_DELETE_OLD_PRODUCTIONS = R"(
        delete from falukant_log.production flp
        where date(flp.production_timestamp) < CURRENT_DATE
    )";

    static constexpr const char *QUERY_GET_PRODUCERS_LAST_DAY = R"(
        select p."producer_id"
        from falukant_log.production p
        where date(p."production_timestamp") = CURRENT_DATE - interval '1 day'
        group by producer_id
    )";

    static constexpr const char *QUERY_UPDATE_REGION_SELL_PRICE = R"(
        UPDATE falukant_data.town_product_worth tpw
        SET worth_percent =
            GREATEST(
                0,
                LEAST(
                    CASE
                        WHEN s.quantity > avg_sells THEN tpw.worth_percent - 1
                        WHEN s.quantity < avg_sells THEN tpw.worth_percent + 1
                        ELSE tpw.worth_percent
                    END,
                    100
                )
            )
        FROM (
            SELECT region_id, product_id, quantity,
                   (SELECT AVG(quantity)
                    FROM falukant_log.sell avs
                    WHERE avs.product_id = s.product_id) AS avg_sells
            FROM falukant_log.sell s
            WHERE DATE(s.sell_timestamp) = CURRENT_DATE - INTERVAL '1 day'
        ) s
        WHERE tpw.region_id = s.region_id
          AND tpw.product_id = s.product_id
    )";

    static constexpr const char *QUERY_DELETE_REGION_SELL_PRICE = R"(
        delete from falukant_log.sell s
        where date(s.sell_timestamp) < CURRENT_DATE
    )";

    static constexpr const char *QUERY_GET_SELL_REGIONS = R"(
        select s."region_id"
        from falukant_log.sell s
        where date(s."sell_timestamp") = CURRENT_DATE - interval '1 day'
        group by "region_id"
    )";

    static constexpr const char * QUERY_SET_MARRIAGES_BY_PARTY = R"(
        WITH updated_relations AS (
            UPDATE falukant_data.relationship AS rel
            SET relationship_type_id = (
                SELECT id
                FROM falukant_type.relationship AS rt
                WHERE rt.tr = 'married'
            )
            WHERE rel.id IN (
                SELECT rel2.id
                FROM falukant_data.party AS p
                JOIN falukant_type.party AS pt
                  ON pt.id = p.party_type_id
                 AND pt.tr = 'wedding'
                JOIN falukant_data.falukant_user AS fu
                  ON fu.id = p.falukant_user_id
                JOIN falukant_data."character" AS c
                  ON c.user_id = fu.id
                JOIN falukant_data.relationship AS rel2
                  ON rel2.character1_id = c.id
                  OR  rel2.character2_id = c.id
                JOIN falukant_type.relationship AS rt2
                  ON rt2.id = rel2.relationship_type_id
                 AND rt2.tr = 'engaged'
                WHERE p.created_at <= NOW() - INTERVAL '1 day'
            )
            RETURNING character1_id, character2_id
        )
        SELECT
            c1.user_id       AS character1_user,
            c2.user_id       AS character2_user
        FROM updated_relations AS ur
        JOIN falukant_data."character" AS c1
          ON c1.id = ur.character1_id
        JOIN falukant_data."character" AS c2
          ON c2.id = ur.character2_id;
    )";

    static constexpr const char * QUERY_GET_STUDYINGS_TO_EXECUTE = R"(
        select l.id, l.associated_falukant_user_id, l.associated_learning_character_id, l.learn_all_products, l.learning_recipient_id, l.product_id,
               lr.tr
        from falukant_data.learning l
        join falukant_type.learn_recipient lr
          on lr.id = l.learning_recipient_id
        where l.learning_is_executed = false
        and l.created_at + interval '1 day' < now();
    )";

    static constexpr const char * QUERY_GET_OWN_CHARACTER_ID = R"(
        select id
        from falukant_data."character" c
        where c.user_id = $1
    )";

    static constexpr const char *QUERY_INCREASE_ONE_PRODUCT_KNOWLEDGE = R"(
        update falukant_data.knowledge k
        set knowledge = LEAST(100, k.knowledge + $1)
        where k.character_id = $2
        and k.product_id = $3
    )";

    static constexpr const char *QUERY_INCREASE_ALL_PRODUCTS_KNOWLEDGE = R"(
        update falukant_data.knowledge k
        set knowledge = LEAST(100, k.knowledge + $1)
        where k.character_id = $2
    )";

    static constexpr const char *QUERY_SET_LEARNING_DONE = R"(
        update falukant_data.learning
        set learning_is_executed = true
        where id = $1
    )";
};

#endif // VALUERECALCULATIONWORKER_H
