#pragma once

#include <maps/libs/geolib/include/bounding_box.h>
#include <maps/libs/geolib/include/multipolygon.h>
#include <maps/libs/geolib/include/polygon.h>
#include <maps/libs/introspection/include/comparison.h>
#include <maps/libs/introspection/include/stream_output.h>
#include <yandex/maps/wiki/social/common.h>
#include <yandex/maps/wiki/social/date_time_condition.h>
#include <yandex/maps/wiki/social/feedback/enums.h>

#include <pqxx/pqxx>

namespace maps::wiki::social::feedback {

class BaseDimensions
{
public:
    std::string whereClause() const;

    BaseDimensions& workflows(std::optional<Workflows> workflows);
    BaseDimensions& types(std::optional<Types> types);
    BaseDimensions& sources(std::optional<std::vector<std::string>> sources);
    BaseDimensions& hidden(std::optional<bool> hidden);

    const std::optional<Workflows>& getWorkflows() const;
    const std::optional<Types>& getTypes() const;
    const std::optional<std::vector<std::string>>& getSources() const;
    const std::optional<bool>& getHidden() const;

    auto introspect() const {
        return std::tie(
            workflows_,
            types_,
            sources_,
            hidden_
        );
    }

private:
    std::optional<Workflows> workflows_;
    std::optional<Types> types_;
    std::optional<std::vector<std::string>> sources_;
    std::optional<bool> hidden_;
};

class TaskFilter
{
public:
    std::string joinClause() const;
    std::string whereClause(pqxx::transaction_base& txn) const;

    TaskFilter& ids(TIds ids);
    TaskFilter& idGreaterThan(TId id);
    TaskFilter& boundary(const geolib3::Polygon2& boundary);
    TaskFilter& boundary(const geolib3::MultiPolygon2& multiPolygon);
    TaskFilter& boxBoundary(const geolib3::BoundingBox& boundingBox);
    TaskFilter& indoorLevel(std::optional<std::string> indoorLevel);
    TaskFilter& acquiredBy(TUid acquiredBy);
    TaskFilter& verdict(std::optional<Verdict> verdict);
    TaskFilter& resolved(bool resolved);
    TaskFilter& resolvedBy(std::optional<TUid> resolvedBy);
    TaskFilter& lastNeedInfoBy(std::optional<TUid> lastNeedInfoBy);
    TaskFilter& modifiedBy(TUid modifiedBy);
    TaskFilter& notResolvedBy(TUids notResolvedBy);
    TaskFilter& resolvedAt(std::optional<DateTimeCondition> dateCondition);
    TaskFilter& lastNeedInfoAt(std::optional<DateTimeCondition> dateCondition);
    TaskFilter& deployed(bool deployed);
    TaskFilter& hidden(std::optional<bool> hidden);
    TaskFilter& internalContent(bool internalContent);
    TaskFilter& type(Type type);
    TaskFilter& types(std::optional<Types> types);
    TaskFilter& ageType(AgeType ageType);
    TaskFilter& ageTypes(std::optional<AgeTypes> ageTypes);
    TaskFilter& workflow(Workflow workflow);
    TaskFilter& workflows(std::optional<Workflows> workflows);
    TaskFilter& bucket(Bucket bucket);
    TaskFilter& buckets(Buckets buckets);
    TaskFilter& duplicateHeadId(std::optional<TId> headId);
    TaskFilter& objectId(TId objectId);
    TaskFilter& duplicateHeadIds(TIds headIds);
    TaskFilter& source(std::string source);
    TaskFilter& sourceNotIn(std::vector<std::string> notSources);
    TaskFilter& sources(std::optional<std::vector<std::string>> sources);
    TaskFilter& experiment(bool isExperiment);
    TaskFilter& stateModifiedAt(DateTimeCondition dateCondition);
    TaskFilter& createdBy(TUid createdBy);
    TaskFilter& createdAt(DateTimeCondition dateCondition);
    TaskFilter& createdBeforeNow();
    TaskFilter& processingLvls(std::optional<ProcessingLvlSet> value);
    TaskFilter& aoiId(TId aoiId);
    TaskFilter& uiFilterStatus(std::optional<UIFilterStatus> uiFilterStatus);
    TaskFilter& addBaseDimensions(BaseDimensions baseDimensions);

    auto introspect() const {
        return std::tie(
            ids_,
            objectId_,
//            boundary_,
//            multiPolygon_,
//            boundingBox_,
            indoorLevel_,
            acquiredBy_,
            resolvedBy_,
            lastNeedInfoBy_,
            modifiedBy_,
            notResolvedBy_,
            verdict_,
            resolved_,
//            resolvedAt_,
//            lastNeedInfoAt_,
            deployed_,
            hidden_,
            internalContent_,
            types_,
            ageTypes_,
            workflows_,
            sources_,
            notSources_,
            isExperiment_,
            buckets_,
            duplicateHeadIds_,
//            stateModifiedAt_,
            createdBy_,
//            createdAt_,
            processingLvls_,
            createdBeforeNow_,
            aoiId_,
            baseDimensionsArray_
        );
    }

private:
    std::optional<TIds> ids_;
    std::optional<TId> idGreaterThan_;
    std::optional<TId> objectId_;
    std::optional<geolib3::Polygon2> boundary_;
    std::optional<geolib3::MultiPolygon2> multiPolygon_;
    std::optional<geolib3::BoundingBox> boundingBox_;
    std::optional<std::string> indoorLevel_;
    std::optional<TUid> acquiredBy_;
    std::optional<TUid> resolvedBy_;
    std::optional<TUid> lastNeedInfoBy_;
    std::optional<TUid> modifiedBy_;
    std::optional<TUids> notResolvedBy_;
    std::optional<Verdict> verdict_;
    std::optional<bool> resolved_;
    std::optional<DateTimeCondition> resolvedAt_;
    std::optional<DateTimeCondition> lastNeedInfoAt_;
    std::optional<bool> deployed_;
    std::optional<bool> hidden_;
    std::optional<bool> internalContent_;
    std::optional<Types> types_;
    std::optional<AgeTypes> ageTypes_;
    std::optional<Workflows> workflows_;
    std::optional<std::vector<std::string>> sources_;
    std::optional<std::vector<std::string>> notSources_;
    std::optional<bool> isExperiment_;
    std::optional<Buckets> buckets_;
    std::optional<TIds> duplicateHeadIds_;
    std::optional<DateTimeCondition> stateModifiedAt_;
    std::optional<TUid> createdBy_;
    std::optional<DateTimeCondition> createdAt_;
    std::optional<ProcessingLvlSet> processingLvls_;
    bool createdBeforeNow_ = false;
    std::optional<TId> aoiId_;
    std::vector<BaseDimensions> baseDimensionsArray_;
};

namespace internal {
std::string BaseDimensionsArrayWhereClause(const std::vector<BaseDimensions>& baseDimensionsArray);
} // namespace internal

} // namespace maps::wiki::social::feedback
