/**
 * @file s3Subscriptions.h
 * @author Bartek Kryza
 * @copyright (C) 2025 ACK CYFRONET AGH
 * @copyright This software is released under the MIT license cited in
 * 'LICENSE.txt'
 */

#ifndef ONECLIENT_S3_SUBSCRIPTIONS_H
#define ONECLIENT_S3_SUBSCRIPTIONS_H

#include "cache/helpersCache.h"
#include "events/declarations.h"
#include "events/streams.h"

#include <folly/FBString.h>

#include <tbb/concurrent_hash_map.h>

namespace one {

namespace client {
namespace events {
class Manager;
class HelperParamsChanged;
} // namespace events
}

namespace s3 {

constexpr std::chrono::milliseconds REMOTE_TIME_THRESHOLD{500};

template <typename T> struct StdHashCompare {
    bool equal(const T &a, const T &b) const { return a == b; }
    std::size_t hash(const T &a) const
    {
        return std::hash<std::pair<std::size_t, std::size_t>>()(
            {static_cast<std::size_t>(a.first),
                std::hash<folly::fbstring>()(a.second)});
    }
};

class S3Subscriptions {
    using Key = std::pair<one::client::events::StreamKey, folly::fbstring>;

public:
    /**
     * Constructor.
     * @param eventManager @c events::Manager instance.
     * @param runInFiber A function that runs callback inside a main fiber.
     */
    S3Subscriptions(one::client::events::Manager &eventManager,
        one::client::cache::HelpersCacheThreadSafeAdapter &helpersCache,
        std::shared_ptr<folly::IOThreadPoolExecutor> executor);

    /**
     * Cancel all subscriptions.
     */
    void unsubscribeAll();

    /**
     * Adds subscription for storage parameter update.
     * @param storageId Storage id.
     */
    void subscribeHelperParamsChanged(const folly::fbstring &storageId);

    /**
     * Cancels subscription for storage parameter update.
     * @param storageId Storage id.
     * @return true if subscription has been removed, false if it didn't exist.
     */
    bool unsubscribeHelperParamsChanged(const folly::fbstring &storageId);

    /**
     * Stop handling of events.
     */
    void stop() { m_stopped = true; }

private:
    void subscribe(const folly::fbstring &fileUuid,
        const one::client::events::Subscription &subscription);
    bool isSubscribed(one::client::events::StreamKey streamKey,
        const folly::fbstring &fileUuid) const;
    bool unsubscribe(one::client::events::StreamKey streamKey,
        const folly::fbstring &fileUuid);

    void handleHelperParamsChangedSubscription(
        one::client::events::Events<one::client::events::HelperParamsChanged>
            events);

    one::client::events::Manager &m_eventManager;

    one::client::cache::HelpersCacheThreadSafeAdapter &m_helpersCache;

    std::shared_ptr<folly::IOThreadPoolExecutor> m_executor;

    // Holds a mapping of specific subscription keys (composed of stream type
    // and file uuid) to integer subscription id's generated by stream manager
    tbb::concurrent_hash_map<Key, std::int64_t, StdHashCompare<Key>>
        m_subscriptions;

    using SubscriptionAcc = typename decltype(m_subscriptions)::accessor;

    bool m_stopped{false};
};

} // namespace s3
} // namespace one

#endif // ONECLIENT_S3_SUBSCRIPTIONS_H
