#include "tools.h"

#include <maps/libs/common/include/base64.h>
#include <maps/libs/http/include/http.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/algorithm/retry.h>

#include <boost/algorithm/string.hpp>

namespace maps::mrc::tools {
namespace {

const std::map<std::string, traffic_signs::TrafficSign> TRAFFIC_SIGN_NAME_TO_TYPE = {
    {"sign_euro_D1_proceed_right", traffic_signs::TrafficSign::MandatoryTurnRight},
    {"sign_euro_D1_proceed_left", traffic_signs::TrafficSign::MandatoryTurnLeft},
    {"sign_il_37", traffic_signs::TrafficSign::PriorityStopGesture},
    {"sign_ru_1.4.1", traffic_signs::TrafficSign::WarningLevelCrossingCountdown3R},
    {"sign_ru_1.4.2", traffic_signs::TrafficSign::WarningLevelCrossingCountdown2R},
    {"sign_ru_1.4.3", traffic_signs::TrafficSign::WarningLevelCrossingCountdown1R},
    {"sign_ru_1.4.4", traffic_signs::TrafficSign::WarningLevelCrossingCountdown3L},
    {"sign_ru_1.4.5", traffic_signs::TrafficSign::WarningLevelCrossingCountdown2L},
    {"sign_ru_1.4.6", traffic_signs::TrafficSign::WarningLevelCrossingCountdown1L},
    {"sign_ru_1.5", traffic_signs::TrafficSign::WarningTramCrossingAhead},
    {"sign_ru_1.6", traffic_signs::TrafficSign::WarningCrossroadsWithPriorityToTheRight},
    {"sign_ru_1.7", traffic_signs::TrafficSign::WarningRoundaboutAhead},
    {"sign_ru_1.8", traffic_signs::TrafficSign::WarningTrafficSignalsAhead},
    {"sign_ru_1.9", traffic_signs::TrafficSign::WarningOpeningOrSwingBridge},
    {"sign_ru_1.10", traffic_signs::TrafficSign::WarningUnprotectedQuaysideOrRiverbank},
    {"sign_ru_1.11.1", traffic_signs::TrafficSign::WarningDangerousBendR},
    {"sign_ru_1.11.2", traffic_signs::TrafficSign::WarningDangerousBendL},
    {"sign_ru_1.12.1", traffic_signs::TrafficSign::WarningDangerousBendsR},
    {"sign_ru_1.12.2", traffic_signs::TrafficSign::WarningDangerousBendsL},
    {"sign_ru_1.13", traffic_signs::TrafficSign::WarningSteepDescent},
    {"sign_ru_1.14", traffic_signs::TrafficSign::WarningSteepAscent},
    {"sign_ru_1.15", traffic_signs::TrafficSign::WarningSlipperyRoadSurface},
    {"sign_ru_1.16", traffic_signs::TrafficSign::WarningUnevenRoad},
    {"sign_ru_1.17", traffic_signs::TrafficSign::WarningUnevenRoadAhead},
    {"sign_ru_1.18", traffic_signs::TrafficSign::WarningLooseChippings},
    {"sign_ru_1.19", traffic_signs::TrafficSign::WarningDangerousRoadside},
    {"sign_ru_1.20.1", traffic_signs::TrafficSign::WarningRoadNarrowsOnBoth},
    {"sign_ru_1.20.2", traffic_signs::TrafficSign::WarningRoadNarrowsOnRight},
    {"sign_ru_1.20.3", traffic_signs::TrafficSign::WarningRoadNarrowsOnLeft},
    {"sign_ru_1.21", traffic_signs::TrafficSign::WarningTwoWayTraffic},
    {"sign_ru_1.22", traffic_signs::TrafficSign::WarningPedestrianCrossingAhead},
    {"sign_ru_1.23", traffic_signs::TrafficSign::WarningChildren},
    {"sign_ru_1.24", traffic_signs::TrafficSign::WarningCyclists},
    {"sign_ru_1.25", traffic_signs::TrafficSign::WarningRoadworksAhead},
    {"sign_ru_1.26", traffic_signs::TrafficSign::WarningDomesticAnimals},
    {"sign_ru_1.27", traffic_signs::TrafficSign::WarningWildAnimals},
    {"sign_ru_1.28", traffic_signs::TrafficSign::WarningFallingRocksOrDebris},
    {"sign_ru_1.29", traffic_signs::TrafficSign::WarningDangerousCrosswinds},
    {"sign_ru_1.30", traffic_signs::TrafficSign::WarningLowFlyingAircraft},
    {"sign_ru_1.31", traffic_signs::TrafficSign::WarningTunnelAhead},
    {"sign_ru_1.32", traffic_signs::TrafficSign::WarningTrafficJam},
    {"sign_ru_1.33", traffic_signs::TrafficSign::WarningOtherDangers},
    {"sign_ru_1.34.1", traffic_signs::TrafficSign::WarningTurningDirectionR},
    {"sign_ru_1.34.2", traffic_signs::TrafficSign::WarningTurningDirectionL},
    {"sign_ru_1.34.3", traffic_signs::TrafficSign::WarningTurningDirectionRL},
    {"sign_ru_2.1", traffic_signs::TrafficSign::PriorityPriorityRoad},
    {"sign_ru_2.2", traffic_signs::TrafficSign::PriorityEofPriorityRoad},
    {"sign_ru_2.3.1", traffic_signs::TrafficSign::WarningCrossroadsMinorRoadRL},
    {"sign_ru_2.3.2", traffic_signs::TrafficSign::WarningCrossroadsMinorRoadR},
    {"sign_ru_2.3.3", traffic_signs::TrafficSign::WarningCrossroadsMinorRoadL},
    {"sign_ru_2.3.4", traffic_signs::TrafficSign::WarningCrossroadsMinorRoadFr},
    {"sign_ru_2.3.5", traffic_signs::TrafficSign::WarningCrossroadsMinorRoadFl},
    {"sign_ru_2.3.6", traffic_signs::TrafficSign::WarningCrossroadsMinorRoadBr},
    {"sign_ru_2.3.7", traffic_signs::TrafficSign::WarningCrossroadsMinorRoadBl},
    {"sign_ru_2.4", traffic_signs::TrafficSign::PriorityGiveWay},
    {"sign_ru_2.5", traffic_signs::TrafficSign::PriorityStop},
    {"sign_ru_2.6", traffic_signs::TrafficSign::PriorityOncomingVehicles},
    {"sign_ru_2.7", traffic_signs::TrafficSign::PriorityOverOncomingVehicles},
    {"sign_ru_3.1", traffic_signs::TrafficSign::ProhibitoryNoEntry},
    {"sign_ru_3.2", traffic_signs::TrafficSign::ProhibitoryNoVehicles},
    {"sign_ru_3.3", traffic_signs::TrafficSign::ProhibitoryNoMotorVehicles},
    {"sign_ru_3.4", traffic_signs::TrafficSign::ProhibitoryNoHeavyGoodsVehicles},
    {"sign_ru_3.5", traffic_signs::TrafficSign::ProhibitoryNoMotorcycles},
    {"sign_ru_3.6", traffic_signs::TrafficSign::ProhibitoryNoTractors},
    {"sign_ru_3.7", traffic_signs::TrafficSign::ProhibitoryNoTrailer},
    {"sign_ru_3.8", traffic_signs::TrafficSign::ProhibitoryNoHorseCarriages},
    {"sign_ru_3.9", traffic_signs::TrafficSign::ProhibitoryNoBicycles},
    {"sign_ru_3.10", traffic_signs::TrafficSign::ProhibitoryNoPedestrians},
    {"sign_ru_3.11", traffic_signs::TrafficSign::ProhibitoryMaxWeight},
    {"sign_ru_3.12", traffic_signs::TrafficSign::ProhibitoryMaxWeightPerAxle},
    {"sign_ru_3.13", traffic_signs::TrafficSign::ProhibitoryMaxHeight},
    {"sign_ru_3.14", traffic_signs::TrafficSign::ProhibitoryMaxWidth},
    {"sign_ru_3.15", traffic_signs::TrafficSign::ProhibitoryMaxLength},
    {"sign_ru_3.16", traffic_signs::TrafficSign::ProhibitoryMinDistance},
    {"sign_ru_3.17.1", traffic_signs::TrafficSign::ProhibitoryCustoms},
    {"sign_ru_3.17.2", traffic_signs::TrafficSign::ProhibitoryDanger},
    {"sign_ru_3.17.3", traffic_signs::TrafficSign::ProhibitoryStop},
    {"sign_ru_3.18.1", traffic_signs::TrafficSign::ProhibitoryNoRightTurn},
    {"sign_ru_3.18.2", traffic_signs::TrafficSign::ProhibitoryNoLeftTurn},
    {"sign_ru_3.19", traffic_signs::TrafficSign::ProhibitoryNoUturn},
    {"sign_ru_3.20", traffic_signs::TrafficSign::ProhibitoryNoOvertaking},
    {"sign_ru_3.21", traffic_signs::TrafficSign::ProhibitoryEofNoOvertaking},
    {"sign_ru_3.22", traffic_signs::TrafficSign::ProhibitoryNoOvertakingByHeavyVehicle},
    {"sign_ru_3.23", traffic_signs::TrafficSign::ProhibitoryEofNoOvertakingByHeavyVehicle},
    {"sign_ru_3.24_n5", traffic_signs::TrafficSign::ProhibitoryMaxSpeed5},
    {"sign_ru_3.24_n10", traffic_signs::TrafficSign::ProhibitoryMaxSpeed10},
    {"sign_ru_3.24_n15", traffic_signs::TrafficSign::ProhibitoryMaxSpeed15},
    {"sign_ru_3.24_n20", traffic_signs::TrafficSign::ProhibitoryMaxSpeed20},
    {"sign_ru_3.24_n25", traffic_signs::TrafficSign::ProhibitoryMaxSpeed25},
    {"sign_ru_3.24_n30", traffic_signs::TrafficSign::ProhibitoryMaxSpeed30},
    {"sign_ru_3.24_n35", traffic_signs::TrafficSign::ProhibitoryMaxSpeed35},
    {"sign_ru_3.24_n40", traffic_signs::TrafficSign::ProhibitoryMaxSpeed40},
    {"sign_ru_3.24_n45", traffic_signs::TrafficSign::ProhibitoryMaxSpeed45},
    {"sign_ru_3.24_n50", traffic_signs::TrafficSign::ProhibitoryMaxSpeed50},
    {"sign_ru_3.24_n55", traffic_signs::TrafficSign::ProhibitoryMaxSpeed55},
    {"sign_ru_3.24_n60", traffic_signs::TrafficSign::ProhibitoryMaxSpeed60},
    {"sign_ru_3.24_n70", traffic_signs::TrafficSign::ProhibitoryMaxSpeed70},
    {"sign_ru_3.24_n80", traffic_signs::TrafficSign::ProhibitoryMaxSpeed80},
    {"sign_ru_3.24_n90", traffic_signs::TrafficSign::ProhibitoryMaxSpeed90},
    {"sign_ru_3.24_n100", traffic_signs::TrafficSign::ProhibitoryMaxSpeed100},
    {"sign_ru_3.24_n110", traffic_signs::TrafficSign::ProhibitoryMaxSpeed110},
    {"sign_ru_3.24_n120", traffic_signs::TrafficSign::ProhibitoryMaxSpeed120},
    {"sign_ru_3.24_n130", traffic_signs::TrafficSign::ProhibitoryMaxSpeed130},
    {"sign_ru_3.25", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed},
    {"sign_ru_3.25_n5", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed5},
    {"sign_ru_3.25_n10", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed10},
    {"sign_ru_3.25_n15", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed15},
    {"sign_ru_3.25_n20", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed20},
    {"sign_ru_3.25_n25", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed25},
    {"sign_ru_3.25_n30", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed30},
    {"sign_ru_3.25_n35", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed35},
    {"sign_ru_3.25_n40", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed40},
    {"sign_ru_3.25_n45", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed45},
    {"sign_ru_3.25_n50", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed50},
    {"sign_ru_3.25_n55", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed55},
    {"sign_ru_3.25_n60", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed60},
    {"sign_ru_3.25_n70", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed70},
    {"sign_ru_3.25_n80", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed80},
    {"sign_ru_3.25_n90", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed90},
    {"sign_ru_3.25_n100", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed100},
    {"sign_ru_3.25_n110", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed110},
    {"sign_ru_3.25_n120", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed120},
    {"sign_ru_3.25_n130", traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed130},
    {"sign_ru_3.26", traffic_signs::TrafficSign::ProhibitoryNoHornsOrMotorNoise},
    {"sign_ru_3.27", traffic_signs::TrafficSign::ProhibitoryNoParkingOrStopping},
    {"sign_ru_3.28", traffic_signs::TrafficSign::ProhibitoryNoParking},
    {"sign_ru_3.29", traffic_signs::TrafficSign::ProhibitoryNoParkingOnOddDaysOfTheMonth},
    {"sign_ru_3.30", traffic_signs::TrafficSign::ProhibitoryNoParkingOnEvenDaysOfTheMonth},
    {"sign_ru_3.31", traffic_signs::TrafficSign::ProhibitoryEofAll},
    {"sign_ru_3.32", traffic_signs::TrafficSign::ProhibitoryNoVehiclesCarryingDangerousGoods},
    {"sign_ru_3.33", traffic_signs::TrafficSign::ProhibitoryNoVehiclesCarryingExplosives},
    {"sign_ru_4.1.1", traffic_signs::TrafficSign::MandatoryProceedStraight},
    {"sign_ru_4.1.2", traffic_signs::TrafficSign::MandatoryTurnRightAhead},
    {"sign_ru_4.1.3", traffic_signs::TrafficSign::MandatoryTurnLeftAhead},
    {"sign_ru_4.1.4", traffic_signs::TrafficSign::MandatoryProceedStraightOrTurnRight},
    {"sign_ru_4.1.5", traffic_signs::TrafficSign::MandatoryProceedStraightOrTurnLeft},
    {"sign_ru_4.1.6", traffic_signs::TrafficSign::MandatoryTurnRightOrLeft},
    {"sign_ru_4.2.1", traffic_signs::TrafficSign::MandatoryDrivingDirectionR},
    {"sign_ru_4.2.2", traffic_signs::TrafficSign::MandatoryDrivingDirectionL},
    {"sign_ru_4.2.3", traffic_signs::TrafficSign::MandatoryDrivingDirectionRL},
    {"sign_ru_4.3", traffic_signs::TrafficSign::MandatoryRoundabout},
    {"sign_ru_4.4.1", traffic_signs::TrafficSign::MandatoryCycleRoute},
    {"sign_ru_4.4.2", traffic_signs::TrafficSign::MandatoryEofCycleRoute},
    {"sign_ru_4.5.1", traffic_signs::TrafficSign::MandatoryPedestrianPath},
    {"sign_ru_4.5.2", traffic_signs::TrafficSign::MandatoryCyclistAndPedestriansCombinedPath},
    {"sign_ru_4.5.3", traffic_signs::TrafficSign::MandatoryEofCyclistAndPedestriansCombinedPath},
    {"sign_ru_4.5.4", traffic_signs::TrafficSign::MandatoryCyclistAndPedestriansSegregatedPath},
    {"sign_ru_4.5.5", traffic_signs::TrafficSign::MandatoryPedestrianAndCyclistsSegregatedPath},
    {"sign_ru_4.5.6", traffic_signs::TrafficSign::MandatoryEofCyclistAndPedestriansSegregatedPath},
    {"sign_ru_4.5.7", traffic_signs::TrafficSign::MandatoryEofPedestrianAndCyclistsSegregatedPath},
    {"sign_ru_4.6", traffic_signs::TrafficSign::MandatoryMinSpeed},
    {"sign_ru_4.7", traffic_signs::TrafficSign::MandatoryEofMinSpeed},
    {"sign_ru_4.8.1", traffic_signs::TrafficSign::MandatoryVehiclesCarryingExplosivesProceedStraight},
    {"sign_ru_4.8.2", traffic_signs::TrafficSign::MandatoryVehiclesCarryingExplosivesTurnRightAhead},
    {"sign_ru_4.8.3", traffic_signs::TrafficSign::MandatoryVehiclesCarryingExplosivesTurnLeftAhead},
    {"sign_ru_5.3", traffic_signs::TrafficSign::PrescriptionLimitedAccessRoad},
    {"sign_ru_5.4", traffic_signs::TrafficSign::PrescriptionEofLimitedAccessRoad},
    {"sign_ru_5.5", traffic_signs::TrafficSign::PrescriptionOneWayRoad},
    {"sign_ru_5.6", traffic_signs::TrafficSign::PrescriptionEofOneWayRoad},
    {"sign_ru_5.7.1", traffic_signs::TrafficSign::PrescriptionEntryToOneWayRoadOnTheRight},
    {"sign_ru_5.7.2", traffic_signs::TrafficSign::PrescriptionEntryToOneWayRoadOnTheLeft},
    {"sign_ru_5.8", traffic_signs::TrafficSign::PrescriptionTwoWayTraffic},
    {"sign_ru_5.9", traffic_signs::TrafficSign::PrescriptionEofTwoWayTraffic},
    {"sign_ru_5.10", traffic_signs::TrafficSign::PrescriptionEntryToTwoWayTraffic},
    {"sign_ru_5.11.1", traffic_signs::TrafficSign::PrescriptionRoadWithBusLane},
    {"sign_ru_5.11.2", traffic_signs::TrafficSign::PrescriptionRoadWithCycleLane},
    {"sign_ru_5.12.1", traffic_signs::TrafficSign::PrescriptionEofRoadWithBusLane},
    {"sign_ru_5.12.2", traffic_signs::TrafficSign::PrescriptionEofRoadWithCycleLane},
    {"sign_ru_5.13.1", traffic_signs::TrafficSign::PrescriptionEntryToRoadWithBusLaneL},
    {"sign_ru_5.13.2", traffic_signs::TrafficSign::PrescriptionEntryToRoadWithBusLaneR},
    {"sign_ru_5.13.3", traffic_signs::TrafficSign::PrescriptionEntryToRoadWithCycleLaneL},
    {"sign_ru_5.13.4", traffic_signs::TrafficSign::PrescriptionEntryToRoadWithCycleLaneR},
    {"sign_ru_5.14", traffic_signs::TrafficSign::PrescriptionBusLane},
    {"sign_ru_5.14.1", traffic_signs::TrafficSign::PrescriptionEofBusLane},
    {"sign_ru_5.14.2", traffic_signs::TrafficSign::PrescriptionCycleLane},
    {"sign_ru_5.14.3", traffic_signs::TrafficSign::PrescriptionEofCycleLane},
    {"sign_ru_5.15.1", traffic_signs::TrafficSign::PrescriptionLanesDirection},
    {"sign_ru_5.15.2_forward", traffic_signs::TrafficSign::PrescriptionLaneDirectionF},
    {"sign_ru_5.15.2_forward_left",
     traffic_signs::TrafficSign::PrescriptionLaneDirectionFL},
    {"sign_ru_5.15.2_forward_or_right_45",
     traffic_signs::TrafficSign::PrescriptionLaneDirectionFFr},
    {"sign_ru_5.15.2_forward_or_left_45",
     traffic_signs::TrafficSign::PrescriptionLaneDirectionFFl},
    {"sign_ru_5.15.2_forward_right",
     traffic_signs::TrafficSign::PrescriptionLaneDirectionFR},
    {"sign_ru_5.15.2_forward_round_left",
     traffic_signs::TrafficSign::PrescriptionLaneDirectionOther},
    {"sign_ru_5.15.2_left", traffic_signs::TrafficSign::PrescriptionLaneDirectionL},
    {"sign_ru_5.15.2_left_45", traffic_signs::TrafficSign::PrescriptionLaneDirectionFl},
    {"sign_ru_5.15.2_right", traffic_signs::TrafficSign::PrescriptionLaneDirectionR},
    {"sign_ru_5.15.2_right_45", traffic_signs::TrafficSign::PrescriptionLaneDirectionFr},
    {"sign_ru_5.15.2_right_45_or_left_45",
     traffic_signs::TrafficSign::PrescriptionLaneDirectionFrFl},
    {"sign_ru_5.15.2_right_left", traffic_signs::TrafficSign::PrescriptionLaneDirectionRL},
    {"sign_ru_5.15.2_right_or_right_45",
     traffic_signs::TrafficSign::PrescriptionLaneDirectionFrR},
    {"sign_ru_5.15.3", traffic_signs::TrafficSign::PrescriptionStartNewLineRight},
    {"sign_ru_5.15.4", traffic_signs::TrafficSign::PrescriptionStartNewLineLeft},
    {"sign_ru_5.15.5", traffic_signs::TrafficSign::PrescriptionEndLineRight},
    {"sign_ru_5.15.6", traffic_signs::TrafficSign::PrescriptionEndLineLeft},
    {"sign_ru_5.15.7", traffic_signs::TrafficSign::PrescriptionTwoWayLanesDirection},
    {"sign_ru_5.16", traffic_signs::TrafficSign::PrescriptionStoppingPlaceForBus},
    {"sign_ru_5.17", traffic_signs::TrafficSign::PrescriptionStoppingPlaceForTrams},
    {"sign_ru_5.18", traffic_signs::TrafficSign::PrescriptionStoppingPlaceForTaxi},
    {"sign_ru_5.19.1", traffic_signs::TrafficSign::PrescriptionPedestrianCrossing},
    {"sign_ru_5.20", traffic_signs::TrafficSign::PrescriptionRoadHump},
    {"sign_ru_5.21", traffic_signs::TrafficSign::PrescriptionLivingZone},
    {"sign_ru_5.22", traffic_signs::TrafficSign::PrescriptionEofLivingZone},
    {"sign_ru_5.23.1", traffic_signs::TrafficSign::PrescriptionBuiltUpArea},
    {"sign_ru_5.24.1", traffic_signs::TrafficSign::PrescriptionEofBuiltUpArea},
    {"sign_ru_6.2", traffic_signs::TrafficSign::InformationAdvisorySpeedLimit},
    {"sign_ru_6.3.1", traffic_signs::TrafficSign::InformationUTurnPlace},
    {"sign_ru_6.3.2", traffic_signs::TrafficSign::InformationUTurnDistance},
    {"sign_ru_6.4", traffic_signs::TrafficSign::InformationParking},
    {"sign_ru_6.5", traffic_signs::TrafficSign::InformationEmergencyStoppingLane},
    {"sign_ru_6.6", traffic_signs::TrafficSign::InformationPedestrianSubway},
    {"sign_ru_6.7", traffic_signs::TrafficSign::InformationFootbridge},
    {"sign_ru_6.8.1", traffic_signs::TrafficSign::InformationCulDeSacF},
    {"sign_ru_6.8.2", traffic_signs::TrafficSign::InformationCulDeSacR},
    {"sign_ru_6.8.3", traffic_signs::TrafficSign::InformationCulDeSacL},
    {"sign_ru_6.10.1", traffic_signs::TrafficSign::InformationDirectionCursor},
    {"sign_ru_6.16", traffic_signs::TrafficSign::InformationStopLine},
    {"sign_ru_7.1", traffic_signs::TrafficSign::InformationFirstAidStation},
    {"sign_ru_7.2", traffic_signs::TrafficSign::InformationHospital},
    {"sign_ru_7.3", traffic_signs::TrafficSign::InformationGasStation},
    {"sign_ru_7.4", traffic_signs::TrafficSign::InformationServiceStation},
    {"sign_ru_7.5", traffic_signs::TrafficSign::InformationCarWash},
    {"sign_ru_7.6", traffic_signs::TrafficSign::InformationPhone},
    {"sign_ru_7.7", traffic_signs::TrafficSign::InformationFood},
    {"sign_ru_7.8", traffic_signs::TrafficSign::InformationDrinkingWater},
    {"sign_ru_7.9", traffic_signs::TrafficSign::InformationHotel},
    {"sign_ru_7.10", traffic_signs::TrafficSign::InformationCamping},
    {"sign_ru_7.11", traffic_signs::TrafficSign::InformationRestingPlace},
    {"sign_ru_7.12", traffic_signs::TrafficSign::InformationTrafficPolice},
    {"sign_ru_7.13", traffic_signs::TrafficSign::InformationPolice},
    {"sign_ru_7.14", traffic_signs::TrafficSign::InformationCheckpointInternationalRoadTransport},
    {"sign_ru_7.15", traffic_signs::TrafficSign::InformationRadio},
    {"sign_ru_7.16", traffic_signs::TrafficSign::InformationRadioCommunicationWithEmergencyServices},
    {"sign_ru_7.17", traffic_signs::TrafficSign::InformationPoolOrBeach},
    {"sign_ru_7.18", traffic_signs::TrafficSign::InformationToilet},
    {"sign_ru_7.19", traffic_signs::TrafficSign::InformationEmergencyTelephone},
    {"sign_ru_7.20", traffic_signs::TrafficSign::InformationFireExtinguisher},
    {"sign_ru_8.1.1", traffic_signs::TrafficSign::InformationDistanceObject},
    {"sign_ru_8.1.2", traffic_signs::TrafficSign::InformationDistanceStopAhead},
    {"sign_ru_8.1.3", traffic_signs::TrafficSign::InformationDistanceObjectRight},
    {"sign_ru_8.1.4", traffic_signs::TrafficSign::InformationDistanceObjectLeft},
    {"sign_ru_8.2.1", traffic_signs::TrafficSign::InformationStartZoneHorizontal},
    {"sign_ru_8.2.2", traffic_signs::TrafficSign::InformationStartZone},
    {"sign_ru_8.2.3", traffic_signs::TrafficSign::InformationEofZone},
    {"sign_ru_8.2.4", traffic_signs::TrafficSign::InformationInZone},
    {"sign_ru_8.3.1", traffic_signs::TrafficSign::InformationDirectionalR},
    {"sign_ru_8.3.2", traffic_signs::TrafficSign::InformationDirectionalL},
    {"sign_ru_8.3.3", traffic_signs::TrafficSign::InformationDirectionalRL},
    {"sign_ru_8.4.1", traffic_signs::TrafficSign::InformationHeavyVehicle},
    {"sign_ru_8.4.2", traffic_signs::TrafficSign::InformationTrailer},
    {"sign_ru_8.4.3", traffic_signs::TrafficSign::InformationLightVehicle},
    {"sign_ru_8.4.4", traffic_signs::TrafficSign::InformationBus},
    {"sign_ru_8.4.5", traffic_signs::TrafficSign::InformationTractor},
    {"sign_ru_8.4.6", traffic_signs::TrafficSign::InformationMotorcycle},
    {"sign_ru_8.4.7", traffic_signs::TrafficSign::InformationBicycle},
    {"sign_ru_8.4.8", traffic_signs::TrafficSign::InformationVehiclesCarryingDangerousGoods},
    {"sign_ru_8.4.9", traffic_signs::TrafficSign::InformationExceptHeavyVehicle},
    {"sign_ru_8.4.10", traffic_signs::TrafficSign::InformationExceptLightVehicle},
    {"sign_ru_8.4.11", traffic_signs::TrafficSign::InformationExceptBus},
    {"sign_ru_8.4.12", traffic_signs::TrafficSign::InformationExceptMotorcycle},
    {"sign_ru_8.4.13", traffic_signs::TrafficSign::InformationExceptBicycle},
    {"sign_ru_8.4.14", traffic_signs::TrafficSign::InformationExceptTaxi},
    {"sign_ru_8.5.1", traffic_signs::TrafficSign::InformationHoliday},
    {"sign_ru_8.5.2", traffic_signs::TrafficSign::InformationWorkingDay},
    {"sign_ru_8.5.4", traffic_signs::TrafficSign::InformationTime},
    {"sign_ru_8.5.5", traffic_signs::TrafficSign::InformationHolidayTime},
    {"sign_ru_8.5.6", traffic_signs::TrafficSign::InformationWorkingDayTime},
    {"sign_ru_8.6.1", traffic_signs::TrafficSign::InformationParkingMethod1},
    {"sign_ru_8.6.2", traffic_signs::TrafficSign::InformationParkingMethod2},
    {"sign_ru_8.6.3", traffic_signs::TrafficSign::InformationParkingMethod3},
    {"sign_ru_8.6.4", traffic_signs::TrafficSign::InformationParkingMethod4},
    {"sign_ru_8.6.5", traffic_signs::TrafficSign::InformationParkingMethod5},
    {"sign_ru_8.6.6", traffic_signs::TrafficSign::InformationParkingMethod6},
    {"sign_ru_8.6.7", traffic_signs::TrafficSign::InformationParkingMethod7},
    {"sign_ru_8.6.8", traffic_signs::TrafficSign::InformationParkingMethod8},
    {"sign_ru_8.6.9", traffic_signs::TrafficSign::InformationParkingMethod9},
    {"sign_ru_8.7", traffic_signs::TrafficSign::InformationParkingDeadstick},
    {"sign_ru_8.8", traffic_signs::TrafficSign::InformationPaidServices},
    {"sign_ru_8.9", traffic_signs::TrafficSign::InformationParkingDurationLimit},
    {"sign_ru_8.10", traffic_signs::TrafficSign::InformationPlaceForCarsInspection},
    {"sign_ru_8.11", traffic_signs::TrafficSign::InformationLimitMaximumWeight},
    {"sign_ru_8.12", traffic_signs::TrafficSign::InformationDangerousRoadside},
    {"sign_ru_8.13", traffic_signs::TrafficSign::InformationCourseOfMainRoad},
    {"sign_ru_8.14", traffic_signs::TrafficSign::InformationLane},
    {"sign_ru_8.15", traffic_signs::TrafficSign::InformationBlindPedestrians},
    {"sign_ru_8.16", traffic_signs::TrafficSign::InformationWetCovering},
    {"sign_ru_8.17", traffic_signs::TrafficSign::InformationInvalids},
    {"sign_ru_8.18", traffic_signs::TrafficSign::InformationExceptInvalids},
    {"sign_ru_8.22.1", traffic_signs::TrafficSign::InformationObstacleL},
    {"sign_ru_8.22.2", traffic_signs::TrafficSign::InformationObstacleR},
    {"sign_ru_8.22.3", traffic_signs::TrafficSign::InformationObstacleF},
    {"sign_ru_8.23", traffic_signs::TrafficSign::InformationSpeedCamera},
    {"sign_ru_8.24", traffic_signs::TrafficSign::InformationTowTruck},
};


traffic_signs::TrafficSign parseTrafficSignId(const std::string& name) {
    auto it = TRAFFIC_SIGN_NAME_TO_TYPE.find(name);
    if (it == TRAFFIC_SIGN_NAME_TO_TYPE.end()) {
        return traffic_signs::TrafficSign::Unknown;
    }
    return it->second;
}


struct TolokaAnswer {
    std::string url;
    geolib3::BoundingBox bbox;
    traffic_signs::TrafficSign sign;
};

http::Client& httpClient()
{
    static http::Client singleton;
    return singleton;
}

auto loadBase64Data(const std::string& url)
{
    http::URL urlWrapper{url};
    http::Request request{httpClient(), http::GET, urlWrapper};
    auto response = request.perform();
    if (response.status() == 200) {
        std::string blob = response.readBody();
        return base64Encode(blob);
    }
    else {
        throw Exception{} << "Unexpected status: " << response.status()
                          << " from: " << url;
    }
}

template <typename Functor>
auto retry(Functor&& f) -> decltype(f())
{
    return common::retryOnException<std::exception>(
        common::RetryPolicy()
            .setInitialTimeout(std::chrono::minutes(1))
            .setMaxAttempts(20)
            .setTimeoutBackoff(1),
        std::forward<Functor>(f));
}

template <class Range, class Compare, class Functor>
void forEqualRanges(Range& rng, Compare cmp, Functor f)
{
    auto first = std::begin(rng);
    auto last = std::end(rng);
    std::sort(first, last, cmp);
    while (first != last) {
        auto next = std::adjacent_find(first, last, cmp);
        if (next != last)
            next = std::next(next);
        f(boost::make_iterator_range(first, next));
        first = next;
    }
}

auto parseClassificationResult(const json::Value& val)
{
    std::vector<TolokaAnswer> result;
    for (const auto& item : val) {
        auto inputValues = item["inputValues"];
        auto url = inputValues["image"].as<std::string>();
        auto bbox = geolib3::BoundingBox{
            geolib3::Point2(inputValues["bbox"][0][0].as<float>(),
                            inputValues["bbox"][0][1].as<float>()),
            geolib3::Point2(inputValues["bbox"][1][0].as<float>(),
                            inputValues["bbox"][1][1].as<float>())};
        auto sign = traffic_signs::TrafficSign::Unknown;
        auto outputValues = item["outputValues"];
        if (outputValues.isObject() && outputValues.hasField("sign_id")) {
            auto signId = outputValues["sign_id"];
            if (signId.isString()) {
                sign = parseTrafficSignId(signId.as<std::string>());
            }
        }
        result.push_back({url, bbox, sign});
    }
    return result;
}

/// Merge Toloka results for a single bbox received from multiple users.
template <class TolokaAnswerRange>
traffic_signs::TrafficSign aggregateSign(const TolokaAnswerRange& answers)
{
    // Moore’s Voting Algorithm
    auto result = traffic_signs::TrafficSign::Unknown;
    long count = 0;
    for (const auto& answer : answers) {
        if (count == 0) {
            result = answer.sign;
            count = 1;
        }
        else if (result == answer.sign) {
            ++count;
        }
        else {
            --count;
        }
    }

    auto first = std::begin(answers);
    auto last = std::end(answers);
    count = std::count_if(first, last, [result](const auto& answer) {
        return answer.sign == result;
    });
    return count > std::distance(first, last) / 2
               ? result
               : traffic_signs::TrafficSign::Unknown;
}

} // anonymous namespace

UrlToBase64DataMap loadPhotos(const json::Value& val)
{
    UrlToBase64DataMap result;
    for (const auto& item : val) {
        auto inputValues = item["inputValues"];
        auto url = inputValues["image"].as<std::string>();
        if (result.count(url) == 0) {
            auto base64Data = retry([&] { return loadBase64Data(url); });
            result.insert({url, std::move(base64Data)});
            INFO() << "loaded " << url;
        }
    }
    return result;
}

UrlToSignsMap aggregateSigns(const json::Value& val)
{
    UrlToSignsMap result;
    auto answers = parseClassificationResult(val);
    forEqualRanges(
        answers,
        [](const auto& lhs, const auto& rhs) {
            return std::make_tuple(lhs.url, lhs.bbox.minX(), lhs.bbox.maxX(),
                                   lhs.bbox.minY(), lhs.bbox.maxY())
                   < std::make_tuple(rhs.url, rhs.bbox.minX(),
                                     rhs.bbox.maxX(), rhs.bbox.minY(),
                                     rhs.bbox.maxY());
        },
        [&](const auto& rng) {
            const auto& front = *std::begin(rng);
            result[front.url][front.bbox] = aggregateSign(rng);
        });
    return result;
}

void dump(const UrlToBase64DataMap& urlToBase64DataMap,
          const UrlToSignsMap& urlToSignsMap,
          std::ostream& os)
{
    json::Builder builder(os);
    builder << [&](json::ObjectBuilder b) {
        b["classified_signs"] << [&](json::ArrayBuilder b) {
            for (const auto& urlToBase64Data : urlToBase64DataMap) {
                const auto& url = urlToBase64Data.first;
                const auto& base64Data = urlToBase64Data.second;
                const auto& bboxToSignMap = urlToSignsMap.at(url);

                b << [&](json::ObjectBuilder b) {
                    std::vector<std::string> tokens;
                    boost::split(tokens, url,
                                 [](char c) { return c == '/'; });

                    b["source"]
                        = "https://mrc-browser.maps.yandex.net/feature/"
                          + tokens.back() + "/image";
                    b["image"] = base64Data;
                    b["signs"] << [&](json::ArrayBuilder b) {
                        for (const auto& bboxToSign : bboxToSignMap) {
                            const auto& bbox = bboxToSign.first;
                            const auto& sign = bboxToSign.second;
                            b << [&](json::ObjectBuilder b) {
                                b["sign_id"]
                                    << boost::lexical_cast<std::string>(sign);
                                b["bbox"] << [&](json::ArrayBuilder b) {
                                    b << [&](json::ArrayBuilder b) {
                                        b << round(bbox.minX())
                                          << round(bbox.minY());
                                    };
                                    b << [&](json::ArrayBuilder b) {
                                        b << round(bbox.maxX())
                                          << round(bbox.maxY());
                                    };
                                };
                                b["answer"] << "ok";
                            };
                        }
                    };
                };
            }
        };
    };
}

} // maps::mrc::tools
