#pragma once

#include <yandex/maps/wiki/revision/common.h>
#include <yandex/maps/wiki/revision/branch.h>
#include <yandex/maps/wiki/revision/branch_manager.h>

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

#include <pqxx/pqxx>

#include <unordered_map>
#include <unordered_set>

namespace maps {
namespace wiki {

const std::string VENDOR_YANDEX = "yandex";
const std::string REGION_CIS1 = "cis1";

const std::string EVENT_EXPORTED = "exported";
const std::string EVENT_DEPLOYED_RENDERER = "deployed_renderer";
const std::string EVENT_DEPLOYED_CAMS = "deployed_cams";
const std::string EVENT_DEPLOYED_CARPARKS = "deployed_carparks";
const std::string EVENT_DEPLOYED_GEOCODER = "deployed_geocoder";
const std::string EVENT_DEPLOYED_MTR_EXPORT = "deployed_mtr_export";
const std::string EVENT_DEPLOYED_GRAPH = "deployed_graph";
const std::string EVENT_DEPLOYED_BICYCLE_GRAPH = "deployed_bicycle_graph";
const std::string EVENT_DEPLOYED_PEDESTRIAN_GRAPH = "deployed_pedestrian_graph";

// Full event name is '<base_event>:<region>'
const std::vector<std::string> BRANCH_BASE_EVENTS = {
    EVENT_EXPORTED,
    EVENT_DEPLOYED_RENDERER,
    EVENT_DEPLOYED_CAMS,
    EVENT_DEPLOYED_CARPARKS,
    EVENT_DEPLOYED_GEOCODER,
    EVENT_DEPLOYED_MTR_EXPORT,
    EVENT_DEPLOYED_GRAPH,
    EVENT_DEPLOYED_BICYCLE_GRAPH,
    EVENT_DEPLOYED_PEDESTRIAN_GRAPH,
};

using BranchId = revision::DBID;
using BranchIds = revision::DBIDSet;
using BranchToTime = std::map<BranchId, chrono::TimePoint>; // must be sorted
using BranchToEvents = std::map<BranchId, std::unordered_set<std::string>>; // must be sorted
using PublishedRegions = std::map<std::string, BranchIds>; // let be sorted
using BranchToAttrs = std::unordered_map<BranchId, revision::Attributes>;

class EventsHolder
{
public:
    const std::unordered_map<std::string, BranchToTime>& data() const { return eventToBranchToTime_; }

    void addEvent(const std::string& event, const std::string& region, BranchId branchId, chrono::TimePoint time);

private:
    void addEvent(const std::string& event, BranchId branchId, chrono::TimePoint time);

    std::unordered_map<std::string, BranchToTime> eventToBranchToTime_;
};

class BranchStore
{
public:
    /**
    * Loads all branch events from the database
    */
    explicit BranchStore(pqxx::transaction_base& coreTxn);

    bool empty() const { return branches_.empty(); }

    PublishedRegions publishedRegions() const;

    // For tests
    const BranchToEvents& branchToEvents() const { return branchToEvents_; }
    const BranchToAttrs& attrsToMerge() const { return attrsToMerge_; }

    /**
     * Writes new attributs to the database
     */
    void writeAttributes(pqxx::transaction_base& coreTxn);

    /**
     * Propagate events backwards in time from the last branch to the previous ones
     */
    void mergeEvents(const EventsHolder& eventsHolder);

private:
    void mergeEvent(
        const std::string& event,
        const BranchToTime& branchToTime);

    revision::BranchManager::Branches branches_;
    BranchToEvents branchToEvents_;
    BranchToAttrs attrsToMerge_;
};

} // namespace wiki
} // namespace maps
