#pragma once

#include <maps/wikimap/mapspro/services/mrc/libs/db/include/txn_id.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/eye/verification_source.h>

#include <maps/libs/enum_io/include/enum_io_fwd.h>
#include <maps/libs/sql_chemistry/include/gateway_access.h>

#include <maps/libs/json/include/value.h>

#include <contrib/libs/eigen/Eigen/Geometry>

#include <optional>
#include <string>
#include <utility>
#include <vector>

namespace maps::mrc::db::eye {

class VerifiedDetectionLocation {
public:
    VerifiedDetectionLocation(
            VerificationSource source,
            TId detectionId,
            std::optional<geolib3::Point2> mercatorPos = std::nullopt,
            std::optional<Eigen::Quaterniond> rotation = std::nullopt);

    TId id() const { return id_; }

    VerificationSource source() const { return source_; }

    TId txnId() const { return txnId_; }

    TId detectionId() const { return detectionId_; }

    const std::optional<geolib3::Point2>& mercatorPos() const { return mercatorPos_; }

    VerifiedDetectionLocation& setMercatorPos(const geolib3::Point2& mercatorPos) {
        mercatorPos_ = mercatorPos;
        return *this;
    }

    std::optional<geolib3::Point2> geodeticPos() const {
        if (mercatorPos_.has_value()) {
            return geolib3::convertMercatorToGeodetic(mercatorPos_.value());
        } else {
            return std::nullopt;
        }
    }

    VerifiedDetectionLocation& setGeodeticPos(const geolib3::Point2& geodeticPos) {
        return setMercatorPos(geolib3::convertGeodeticToMercator(geodeticPos));
    }

    std::optional<Eigen::Quaterniond> rotation() const {
        if (rotation_.has_value()) {
            return Eigen::Quaterniond{rotation_.value().data()};
        } else {
            return std::nullopt;
        }
    }

private:
    friend class sql_chemistry::GatewayAccess<VerifiedDetectionLocation>;
    friend class TxnIdAccess<VerifiedDetectionLocation>;

    VerifiedDetectionLocation() {};

    VerifiedDetectionLocation& setTxnId(TId txnId) {
        txnId_ = txnId;
        return *this;
    }

    template <typename T>
    static auto introspect(T& t)
    {
        return std::tie(t.id_, t.source_, t.txnId_, t.detectionId_, t.mercatorPos_, t.rotation_);
    }

    TId id_;
    VerificationSource source_;
    TId txnId_;
    TId detectionId_;
    std::optional<geolib3::Point2> mercatorPos_;
    std::optional<std::vector<double>> rotation_;

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

using VerifiedDetectionLocations = std::vector<VerifiedDetectionLocation>;

} // namespace maps::mrc::db::eye
