#pragma once

#include <maps/libs/chrono/include/time_point.h>
#include <maps/libs/enum_io/include/enum_io_fwd.h>
#include <maps/libs/geolib/include/bounding_box.h>
#include <maps/libs/geolib/include/polygon.h>
#include <maps/wikimap/mapspro/libs/revision/include/yandex/maps/wiki/revision/common.h>

#include <array>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>

namespace maps::wiki::user_edits_metrics {

enum class Event {
     Approved,
     Exported,
     InStable,
     Resolved,

     DeployedBicycleGraph,
     DeployedCams,
     DeployedCarparks,
     DeployedGeocoder,
     DeployedGraph,
     DeployedMtrExport,
     DeployedPedestrianGraph,
     DeployedRenderer,
     DeployedTotal,

     DeployedBicycleGraphBrokenSla,
     DeployedCamsBrokenSla,
     DeployedCarparksBrokenSla,
     DeployedGeocoderBrokenSla,
     DeployedGraphBrokenSla,
     DeployedMtrExportBrokenSla,
     DeployedPedestrianGraphBrokenSla,
     DeployedRendererBrokenSla,
     DeployedTotalBrokenSla,

     DeployedBicycleGraphRealSla,
     DeployedCamsRealSla,
     DeployedCarparksRealSla,
     DeployedGeocoderRealSla,
     DeployedGraphRealSla,
     DeployedMtrExportRealSla,
     DeployedPedestrianGraphRealSla,
     DeployedRendererRealSla,
     DeployedTotalRealSla
};
DECLARE_ENUM_IO(Event);

using EventSet = std::unordered_set<Event>;

// Full event name is '<base_event>:<region>'
const EventSet BRANCH_BASE_EVENTS {
    Event::Exported,
    Event::DeployedRenderer,
    Event::DeployedCams,
    Event::DeployedCarparks,
    Event::DeployedGeocoder,
    Event::DeployedMtrExport,
    Event::DeployedGraph,
    Event::DeployedBicycleGraph,
    Event::DeployedPedestrianGraph,
};

const EventSet NON_BRANCH_BASE_EVENTS {
    Event::Resolved,
    Event::Approved,
    Event::InStable
};

const EventSet DEPLOYED_TO_PRODUCTION_EVENTS {
    Event::DeployedBicycleGraph,
    Event::DeployedCams,
    Event::DeployedCarparks,
    Event::DeployedGeocoder,
    Event::DeployedGraph,
    Event::DeployedMtrExport,
    Event::DeployedPedestrianGraph,
    Event::DeployedRenderer
};

const auto COMMIT_EVENTS = [] {
    EventSet events{NON_BRANCH_BASE_EVENTS.begin(), NON_BRANCH_BASE_EVENTS.end()};
    events.insert(BRANCH_BASE_EVENTS.begin(), BRANCH_BASE_EVENTS.end());
    return events;
}();

const std::unordered_map<Event, Event> EVENT_TO_BROKEN_SLA_EVENT = {
    {Event::DeployedBicycleGraph,    Event::DeployedBicycleGraphBrokenSla},
    {Event::DeployedCams,            Event::DeployedCamsBrokenSla},
    {Event::DeployedCarparks,        Event::DeployedCarparksBrokenSla},
    {Event::DeployedGeocoder,        Event::DeployedGeocoderBrokenSla},
    {Event::DeployedGraph,           Event::DeployedGraphBrokenSla},
    {Event::DeployedMtrExport,       Event::DeployedMtrExportBrokenSla},
    {Event::DeployedPedestrianGraph, Event::DeployedPedestrianGraphBrokenSla},
    {Event::DeployedRenderer,        Event::DeployedRendererBrokenSla},
    {Event::DeployedTotal,           Event::DeployedTotalBrokenSla},
};

const std::unordered_map<Event, Event> EVENT_TO_REAL_SLA_EVENT = {
    {Event::DeployedBicycleGraph,    Event::DeployedBicycleGraphRealSla},
    {Event::DeployedCams,            Event::DeployedCamsRealSla},
    {Event::DeployedCarparks,        Event::DeployedCarparksRealSla},
    {Event::DeployedGeocoder,        Event::DeployedGeocoderRealSla},
    {Event::DeployedGraph,           Event::DeployedGraphRealSla},
    {Event::DeployedMtrExport,       Event::DeployedMtrExportRealSla},
    {Event::DeployedPedestrianGraph, Event::DeployedPedestrianGraphRealSla},
    {Event::DeployedRenderer,        Event::DeployedRendererRealSla},
    {Event::DeployedTotal,           Event::DeployedTotalRealSla},
};

const std::string USER_TYPE_COMMON = "common_users";
const std::string USER_TYPE_TRUSTED = "trusted_users";
const std::string USER_TYPE_NON_YANDEX = "non_yandex_users";
const std::string USER_TYPE_YANDEX = "yandex_users";

const std::array ALL_USER_TYPES = {
    USER_TYPE_COMMON,
    USER_TYPE_TRUSTED,
    USER_TYPE_NON_YANDEX,
    USER_TYPE_YANDEX
};

const std::string ALL_REGIONS_REGION = "all_regions";

enum class Days { AllDays, WorkDays };
DECLARE_ENUM_IO(Days);

using EventToTime = std::unordered_map<Event, chrono::TimePoint>;
using RegionToEventToTime = std::unordered_map<std::string, EventToTime>;
using EventToRegions = std::unordered_map<Event, std::set<std::string>>;
using RegionToEvents = std::unordered_map<std::string, EventSet>;

struct BranchInfo
{
    chrono::TimePoint created;
    RegionToEventToTime regionToEvents;
};

using BranchesById = std::map<revision::DBID, BranchInfo>; // must be sorted
using RegionsData = std::map<revision::DBID, std::string>;
using AoiRegionsData = std::map<std::string, geolib3::Polygon2>;

// For each event occured since minTime inclusively calculate all regions
// where it has occured. So do not calculate metrics for event
// in regions where event has never occured
// Always add aoi regions and 'all_regions' for each event
EventToRegions calcEventToRegions(
    const BranchesById& branchesData,
    const AoiRegionsData& aoiRegionsData,
    const chrono::TimePoint& minTime);

EventToRegions calcEventToRegions(
    const BranchesById& branchesData,
    const chrono::TimePoint& minTime);

EventToRegions calcEventToDeployedRegions(
    const BranchesById& branchesData,
    const chrono::TimePoint& minTime);

RegionToEvents calcRegionToDeployedEvents(const EventToRegions& eventToRegions);

std::string toString(const std::optional<std::chrono::seconds>& duration);

maps::chrono::TimePoint beginningOfTimeInterval(
    maps::chrono::TimePoint time,
    std::chrono::seconds interval);

} // namespace maps::wiki::user_edits_metrics
