#pragma once

#include <maps/wikimap/mapspro/services/mrc/libs/blackbox_client/include/blackbox_client.h>
#include <maps/wikimap/mapspro/services/mrc/libs/config/include/config.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/common.h>

#include <maps/libs/chrono/include/time_point.h>

#include <maps/libs/concurrent/include/lru_cache.h>
#include <maps/libs/pgpool/include/pgpool3.h>

#include <library/cpp/tvmauth/client/facade.h>

#include <functional>
#include <memory>
#include <optional>

namespace maps::mrc::browser {

using namespace std::chrono_literals;

struct IPermissionChecker {
    using Uid = blackbox_client::Uid;
    virtual ~IPermissionChecker() = default;

    virtual bool userMayViewPhotos(std::optional<Uid>, db::FeaturePrivacy) = 0;
};

using IPermissionCheckerHolder = std::unique_ptr<IPermissionChecker>;

using TicketProvider = std::function<std::string()>;

// Check permissions using wiki-acl library
class WikiAclPermissionChecker : public IPermissionChecker {
public:

    WikiAclPermissionChecker(std::string host,
                      TicketProvider tvmTicketProvider,
                      size_t maxCacheSize = 1000u,
                      std::chrono::seconds expirationPeriod = std::chrono::seconds(60))
        : host_(std::move(host))
        , tvmTicketProvider_(tvmTicketProvider)
        , cache_(maxCacheSize)
        , expirationPeriod_(expirationPeriod)
    {
    }

    bool userMayViewPhotos(std::optional<Uid>, db::FeaturePrivacy) override;

private:
    struct TimedPermissions {
        std::vector<db::FeaturePrivacy> allowedPermissions;
        chrono::TimePoint created;
    };

    std::shared_ptr<TimedPermissions> loadUserPermissions(Uid uid);
    std::vector<db::FeaturePrivacy> evalUserPermissions(Uid uid);
    http::URL makeCheckPermissionsUrl(Uid uid) const;
    std::string formatPathsParam() const;

    static const std::map<std::string, db::FeaturePrivacy> permissionPathToPrivacy;

    const std::string host_;
    TicketProvider tvmTicketProvider_;
    http::Client client_;
    concurrent::LruCache<Uid, TimedPermissions> cache_;
    const std::chrono::seconds expirationPeriod_;
};

class PermissionCheckerStub : public IPermissionChecker {
public:
    PermissionCheckerStub() = default;

    bool userMayViewPhotos(std::optional<Uid>, db::FeaturePrivacy) override
    {
        return true;
    }
};


inline IPermissionCheckerHolder makePermissionChecker(
    const common::Config config,
    NTvmAuth::TTvmClient& tvmClient,
    size_t maxCacheSize = 1000u,
    std::chrono::seconds expirationPeriod = 60s)
{
    const auto& wikiAclConfig = config.externals().wikiAcl();

    return std::make_unique<WikiAclPermissionChecker>(
        wikiAclConfig.host(),
        [&]() { return tvmClient.GetServiceTicketFor(wikiAclConfig.tvmId()); },
        maxCacheSize,
        expirationPeriod);
}

} // namespace maps::mrc::browser
