#pragma once

#include <maps/wikimap/mapspro/services/editor/src/execution_state.h>
#include <maps/wikimap/mapspro/services/editor/src/common.h>
#include <yandex/maps/wiki/revision/branch.h>

namespace maps {
namespace wiki {
namespace sync {

const std::string STAGES_VIEW = "view";
const std::string STAGES_ATTRS = "attrs";
const std::string STAGES_SUGGEST = "suggest";
const std::string STAGES_LABELS = "labels";
const std::string STAGES_BBOX = "bbox";
const std::string STAGES_BBOX_HISTORY = "bbox-history";
const std::string STAGES_BBOX_VIEW = "bbox-view";
const std::string STAGES_ALL = "all";

const std::string STR_ALL_CATEGORY_GROUPS = "all";
const std::string STR_ALL_VIEW_TABLES = "all";
constexpr size_t DEFAULT_BATCH_SIZE = 0;
constexpr size_t AUTO_THREAD_COUNT = 0;
constexpr size_t DEFAULT_COMMIT_RANGE_BATCH_SIZE = 10000;

class SyncFlags
{
public:
    explicit SyncFlags(const std::string& stages);
    explicit SyncFlags(const StringSet& stages);

    bool all() const
    {
        return view() && attrs() && labels() && bboxAll();
    }

    bool view() const { return view_; }
    bool attrs() const { return attrs_; }
    bool suggest() const { return suggest_; }
    bool labels() const { return labels_; }

    bool bboxAll() const { return bboxHistory_ && bboxView_; }
    bool bboxAny() const { return bboxHistory_ || bboxView_; }
    bool bboxHistory() const { return bboxHistory_; }
    bool bboxView() const { return bboxView_; }

private:
    void init(const StringSet& stages);

    bool view_;
    bool attrs_;
    bool suggest_;
    bool labels_;
    bool bboxHistory_;
    bool bboxView_;
};

std::ostream& operator << (std::ostream& out, const SyncFlags& flags);

struct CustomIds
{
    CustomIds() = default;

    CustomIds(TCommitIds commitIds)
        : commitIds(std::move(commitIds))
    {}

    CustomIds(TCommitIds commitIds, TOIds objectIds)
        : commitIds(std::move(commitIds))
        , objectIds(std::move(objectIds))
    {}

    bool empty() const
    {
        return commitIds.empty() && objectIds.empty();
    }

    TCommitIds commitIds;
    TOIds objectIds;
};

enum class SetProgressState { Yes, No };
enum class BigData { Yes, No };

class SyncParams
{
public:
    //Easy sync with default parameters
    SyncParams(
        const ExecutionStatePtr& executionState,
        SyncFlags syncFlags,
        TBranchId branchId,
        size_t threads = AUTO_THREAD_COUNT);

    //Intened to use in tests: sync all stages for all objects
    SyncParams(
        const ExecutionStatePtr& executionState,
        TBranchId branchId,
        SetProgressState setProgressState,
        size_t threads = AUTO_THREAD_COUNT);

    SyncParams(
        const ExecutionStatePtr& executionState,
        const std::string& stages,
        const std::string& categoryGroups,
        const std::string& categories,
        const std::string& viewTables,
        const std::string& branchStr,
        revision::Branch::LockType lockType,
        SetProgressState setProgressState,
        size_t batchSize,
        size_t threads,
        BigData bigData);

    //Call this after branch lock
    revision::Branch updateBranch(Transaction& work);
    void updateHeadCommitId(Transaction& work);

    const revision::Branch& branch() const { return branch_; }
    TCommitId headCommitId() const;

    SetProgressState setProgressState() const { return setProgressState_; }
    revision::Branch::LockType branchLockType() const { return branchLockType_; }

    const SyncFlags& syncFlags() const { return syncFlags_; }
    const ExecutionStatePtr& executionState() const { return executionState_; }

    bool hasCustomViewTableNames() const { return customViewTableNames_.is_initialized(); }
    bool hasCustomViewTable(const std::string& tableName) const;

    bool isCustomCategoryIds() const { return customCategoryIds_.is_initialized(); }
    StringSet objectsCategories(const StringSet& availableCategories) const;
    StringSet customCategories() const;

    size_t batchSize() const { return batchSize_; }
    size_t threads() const { return threads_; }

    //Bbox params
    BigData bigData() const { return bigData_; }

    size_t commitRangeBatchSize() const { return commitRangeBatchSize_; }
    void setCommitRangeBatchSize(size_t size) { commitRangeBatchSize_ = size; }

private:
    void initCustomViewTables(const std::string& viewTables);
    void initCustomCategories(
        const std::string& categoryGroups,
        const std::string& categories);

    StringSet objectCategoriesFromViewTables() const;

    ExecutionStatePtr executionState_;
    const SyncFlags syncFlags_;

    revision::Branch branch_;
    boost::optional<TCommitId> headCommitId_;

    const revision::Branch::LockType branchLockType_;
    const SetProgressState setProgressState_;

    boost::optional<StringSet> customViewTableNames_;
    boost::optional<StringSet> customCategoryIds_;

    const size_t batchSize_;
    const size_t threads_;

    const BigData bigData_;
    size_t commitRangeBatchSize_;
};

} // namespace sync
} // namespace wiki
} // namespace maps
