#pragma once

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

#include <maps/libs/sql_chemistry/include/gateway_access.h>
#include <maps/libs/enum_io/include/enum_io_fwd.h>
#include <maps/libs/geolib/include/polyline.h>
#include <maps/libs/common/include/exception.h>

namespace maps::mrc::db::ugc {

enum class TolokaStatus {
    InProgress,
    Accepted,
    Rejected,
};

DECLARE_ENUM_IO(TolokaStatus);

class AssignmentReview {
public:
    explicit AssignmentReview(TId assignmentId)
        : assignmentId_(assignmentId)
        , tolokaStatus_(TolokaStatus::InProgress) {
    }

    TId assignmentId() const { return assignmentId_; }

    TolokaStatus tolokaStatus() const { return tolokaStatus_; }

    /// Fraction of the photographed part of the task [0(bad), 1(good)]
    double coverageFraction() const { return coverageFraction_; }

    std::optional<double> goodCoverageFraction() const { return goodCoverageFraction_; }

    std::optional<double> trackDistanceInMeters() const { return trackDistanceInMeters_; }

    std::optional<std::chrono::seconds> trackDuration() const {
        // Field trackDuration maps on bigint column.
        if (trackDuration_) { return std::chrono::seconds{*trackDuration_}; }
        return std::nullopt;
    }

    const OptionalTimePoint& actualizationDate() const { return actualizationDate_; }

    std::optional<size_t> processedPhotos() const { return processedPhotos_; }

    std::optional<size_t> processedPoints() const { return processedPoints_; }

    std::optional<size_t> goodPhotos() const { return goodPhotos_; }

    const OptionalTimePoint& firstShotTime() const { return firstShotTime_; }

    const OptionalTimePoint& lastShotTime() const { return lastShotTime_; }

    const std::optional<geolib3::PolylinesVector>& coveredGeodeticGeom() const {
        return coveredGeodeticGeom_;
    }

    const std::optional<geolib3::PolylinesVector>& uncoveredGeodeticGeom() const {
        return uncoveredGeodeticGeom_;
    }

    const std::optional<geolib3::PolylinesVector>& trackGeodeticGeom() const {
        return trackGeodeticGeom_;
    }

    CameraDeviation cameraDeviation() const
    {
        return static_cast<CameraDeviation>(cameraDeviation_);
    }



    AssignmentReview& setTolokaStatus(TolokaStatus status) {
        tolokaStatus_ = status;
        return *this;
    }

    AssignmentReview& setCoverageFraction(double fraction) {
        REQUIRE(fraction >= 0 && fraction <= 1,
            "Fraction " << fraction << " should be in [0, 1] range.");
        coverageFraction_ = fraction;
        return *this;
    }

    AssignmentReview& setGoodCoverageFraction(double fraction) {
        goodCoverageFraction_ = fraction;
        return *this;
    }

    AssignmentReview& setTrackDistanceInMeters(double distanceMeters) {
        trackDistanceInMeters_ = distanceMeters;
        return *this;
    }

    AssignmentReview& setTrackDuration(std::chrono::seconds duration) {
        trackDuration_ = duration.count();
        return *this;
    }

    AssignmentReview& setActualizationDate(chrono::TimePoint date) {
        actualizationDate_ = date;
        return *this;
    }

    AssignmentReview& setProcessedPhotos(size_t count) {
        processedPhotos_ = count;
        return *this;
    }

    AssignmentReview& setProcessedPoints(size_t count) {
        processedPoints_ = count;
        return *this;
    }

    AssignmentReview& setGoodPhotos(size_t count) {
        goodPhotos_ = count;
        return *this;
    }

    AssignmentReview& setFirstShotTime(chrono::TimePoint timePoint) {
        firstShotTime_ = timePoint;
        return *this;
    }

    AssignmentReview& setLastShotTime(chrono::TimePoint timePoint) {
        lastShotTime_ = timePoint;
        return *this;
    }

    AssignmentReview& setCoveredGeodeticGeom(geolib3::PolylinesVector geom) {
        coveredGeodeticGeom_ = std::move(geom);
        return *this;
    }

    AssignmentReview& setUncoveredGeodeticGeom(geolib3::PolylinesVector geom) {
        uncoveredGeodeticGeom_ = std::move(geom);
        return *this;
    }

    AssignmentReview& setTrackGeodeticGeom(geolib3::PolylinesVector geom) {
        trackGeodeticGeom_ = std::move(geom);
        return *this;
    }

    AssignmentReview& setCameraDeviation(CameraDeviation cameraDeviation) {
        cameraDeviation_ = static_cast<int16_t>(cameraDeviation);
        return *this;
    }

private:
    friend class sql_chemistry::GatewayAccess<AssignmentReview>;
    AssignmentReview() = default;

    template <typename T>
    static auto introspect(T& t) {
        return std::tie(t.assignmentId_, t.tolokaStatus_, t.coverageFraction_,
            t.goodCoverageFraction_, t.trackDistanceInMeters_, t.trackDuration_,
            t.actualizationDate_, t.processedPhotos_, t.processedPoints_, t.goodPhotos_,
            t.firstShotTime_, t.lastShotTime_, t.coveredGeodeticGeom_, t.uncoveredGeodeticGeom_,
            t.trackGeodeticGeom_, t.cameraDeviation_);
    }

    TId assignmentId_{};
    TolokaStatus tolokaStatus_{};
    double coverageFraction_{};
    std::optional<double> goodCoverageFraction_{};
    std::optional<double> trackDistanceInMeters_{};
    std::optional<int64_t> trackDuration_{};
    std::optional<chrono::TimePoint> actualizationDate_{};
    std::optional<size_t> processedPhotos_{};
    std::optional<size_t> processedPoints_{};
    std::optional<size_t> goodPhotos_{};
    std::optional<chrono::TimePoint> firstShotTime_{};
    std::optional<chrono::TimePoint> lastShotTime_{};
    std::optional<geolib3::PolylinesVector> coveredGeodeticGeom_{};
    std::optional<geolib3::PolylinesVector> uncoveredGeodeticGeom_{};
    std::optional<geolib3::PolylinesVector> trackGeodeticGeom_{};
    int16_t cameraDeviation_{0};

public:
    auto introspect() const { return introspect(*this); }
};

using AssignmentReviews = std::vector<AssignmentReview>;

} // namespace maps::mrc::db::ugc
