#pragma once

#include <maps/wikimap/mapspro/services/editor/src/common.h>

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

#include <optional>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>

namespace maps::wiki::tests {

struct Coordinates {
    Coordinates() = default;
    Coordinates(const Coordinates&) = default;
    Coordinates(Coordinates&&) = default;
    Coordinates& operator=(const Coordinates&) = default;
    Coordinates& operator=(Coordinates&&) = default;

    Coordinates(double x, double y);
    Coordinates(const json::Value& value);

    void json(json::ArrayBuilder builder) const;

    double x = 0;
    double y = 0;
};
using CoordinatesVec = std::vector<Coordinates>;


struct Point: public Coordinates {
    Point() = default;
    Point(const Point&) = default;
    Point(Point&&) = default;
    Point& operator=(const Point&) = default;
    Point& operator=(Point&&) = default;

    Point(Coordinates coordinates);
    Point(const json::Value& value);

    void json(json::ObjectBuilder builder) const;
};


struct LineString {
    LineString() = default;
    LineString(const LineString&) = default;
    LineString(LineString&&) = default;
    LineString& operator=(const LineString&) = default;
    LineString& operator=(LineString&&) = default;

    LineString(CoordinatesVec coordinates);
    LineString(const json::Value& value);

    void json(json::ObjectBuilder builder) const;

    CoordinatesVec coordinates = CoordinatesVec(2);
};


struct Polygon {
    Polygon() = default;
    Polygon(const Polygon&) = default;
    Polygon(Polygon&&) = default;
    Polygon& operator=(const Polygon&) = default;
    Polygon& operator=(Polygon&&) = default;

    Polygon(std::vector<CoordinatesVec> coordinates);
    Polygon(const json::Value value);

    void json(json::ObjectBuilder builder) const;

    std::vector<CoordinatesVec> coordinates = {CoordinatesVec(4)};
};


struct Geometry: public std::variant<Point, LineString, Polygon> {
    Geometry() = default;
    Geometry(const Geometry&) = default;
    Geometry(Geometry&&) = default;
    Geometry& operator=(const Geometry&) = default;
    Geometry& operator=(Geometry&&) = default;

    template<typename GeometryType>
    Geometry(GeometryType geometry)
        : std::variant<Point, LineString, Polygon>(std::move(geometry))
    {}

    Geometry(const json::Value& value);

    void json(json::ObjectBuilder builder) const;
};


struct EditContext {
    void json(json::ObjectBuilder builder) const;

    int zoom = 0;
    Point center;
};


class ObjectsCreator {
public:
    operator std::string() const;

    ObjectsCreator&
    categoryId(std::string newCategoryId) { std::swap(categoryId_, newCategoryId); return *this; }

    ObjectsCreator&
    geometry(Geometry newGeometry) { std::swap(geometry_, newGeometry); return *this; }

    ObjectsCreator&
    revisionId(std::string newRevisionId) { revisionId_ = std::move(newRevisionId); return *this; }

    ObjectsCreator&
    attr(std::string name, std::string value) { attributes_.insert_or_assign(std::move(name), std::move(value)); return *this; }

    void json(json::ObjectBuilder builder) const;

private:
    std::string categoryId_;
    EditContext editContext_;
    std::optional<std::string> revisionId_;
    Geometry geometry_;
    std::unordered_map<std::string, std::string> attributes_;
};


ObjectsCreator aoi();           // autoapprove = true
ObjectsCreator building();      // autoapprove = false
ObjectsCreator poiFood();       // autoapprove = false
ObjectsCreator rdEl();          // autoapprove = false
ObjectsCreator rdJc();          // autoapprove = false

} // namespace maps::wiki::tests
