// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

///
/// \file   testActivityHelpers.cxx
/// \author Piotr Konopka
///

#include "QualityControl/ActivityHelpers.h"
#include "QualityControl/ObjectMetadataKeys.h"
#include "QualityControl/QcInfoLogger.h"

#include <boost/property_tree/ptree.hpp>
#include <catch_amalgamated.hpp>

using namespace o2::quality_control::core;
using namespace o2::quality_control::repository;

// The code below includes parts generated by AI
TEST_CASE("activity_helpers_asDatabaseMetadata")
{

  SECTION("Default values")
  {
    Activity defaultActivity;

    auto metadata = activity_helpers::asDatabaseMetadata(defaultActivity, true);
    REQUIRE(metadata[metadata_keys::runType] == "0");
    REQUIRE(metadata[metadata_keys::runNumber] == "0");
    REQUIRE(metadata[metadata_keys::passName].empty());
    REQUIRE(metadata[metadata_keys::periodName].empty());
  }

  SECTION("Non-default values")
  {
    Activity customActivity;
    customActivity.mType = 2;
    customActivity.mId = 3;
    customActivity.mPassName = "pass";
    customActivity.mPeriodName = "period";

    auto metadata = activity_helpers::asDatabaseMetadata(customActivity, false);
    CHECK(metadata[metadata_keys::runType] == "2");
    CHECK(metadata[metadata_keys::runNumber] == "3");
    CHECK(metadata[metadata_keys::passName] == "pass");
    CHECK(metadata[metadata_keys::periodName] == "period");
  }

  SECTION("Default values not stored when putDefault is false")
  {
    Activity defaultActivity;

    auto metadata = activity_helpers::asDatabaseMetadata(defaultActivity, false);
    REQUIRE(metadata.empty());
  }
}

TEST_CASE("activity_helpers_asActivity")
{

  SECTION("Empty metadata map")
  {
    std::map<std::string, std::string> emptyMetadata;

    Activity activity = activity_helpers::asActivity(emptyMetadata, "test_provenance");

    CHECK(activity.mType == 0);
    CHECK(activity.mId == 0);
    CHECK(activity.mPassName.empty());
    CHECK(activity.mPeriodName.empty());
    CHECK(activity.mProvenance == "test_provenance");
  }

  SECTION("Non-empty metadata map")
  {
    std::map<std::string, std::string> metadata = {
      { metadata_keys::runType, "2" },
      { metadata_keys::runNumber, "3" },
      { metadata_keys::passName, "pass" },
      { metadata_keys::periodName, "period" },
      { metadata_keys::validFrom, "1000000" },
      { metadata_keys::validUntil, "2000000" }
    };

    Activity activity = activity_helpers::asActivity(metadata, "test_provenance");

    CHECK(activity.mType == 2);
    CHECK(activity.mId == 3);
    CHECK(activity.mPassName == "pass");
    CHECK(activity.mPeriodName == "period");
    CHECK(activity.mValidity.getMin() == 1000000);
    CHECK(activity.mValidity.getMax() == 2000000);
    CHECK(activity.mProvenance == "test_provenance");
  }
}

TEST_CASE("activity_helpers_asActivityPtree")
{

  SECTION("Empty property tree")
  {
    boost::property_tree::ptree emptyTree;

    Activity activity = activity_helpers::asActivity(emptyTree, "test_provenance");

    CHECK(activity.mType == 0);
    CHECK(activity.mId == 0);
    CHECK(activity.mPassName.empty());
    CHECK(activity.mPeriodName.empty());
    CHECK(activity.mProvenance == "test_provenance");
  }

  SECTION("Non-empty property tree")
  {
    boost::property_tree::ptree tree;
    tree.put(metadata_keys::runType, 2);
    tree.put(metadata_keys::runNumber, 3);
    tree.put(metadata_keys::passName, "pass");
    tree.put(metadata_keys::periodName, "period");
    tree.put(metadata_keys::validFrom, 1000000);
    tree.put(metadata_keys::validUntil, 2000000);

    Activity activity = activity_helpers::asActivity(tree, "test_provenance");

    CHECK(activity.mType == 2);
    CHECK(activity.mId == 3);
    CHECK(activity.mPassName == "pass");
    CHECK(activity.mPeriodName == "period");
    CHECK(activity.mValidity.getMin() == 1000000);
    CHECK(activity.mValidity.getMax() == 2000000);
    CHECK(activity.mProvenance == "test_provenance");
  }
}

TEST_CASE("test_strictestMatchingActivity")
{
  {
    // providing a map accessor + everything being the same except the validity
    std::map<int, Activity> m{
      { 1, { 300000, 1, "LHC22a", "spass", "qc", { 1, 10 }, "pp" } },
      { 2, { 300000, 1, "LHC22a", "spass", "qc", { 10, 20 }, "pp" } },
      { 4, { 300000, 1, "LHC22a", "spass", "qc", { 20, 30 }, "pp" } },
      { 3, { 300000, 1, "LHC22a", "spass", "qc", { 30, 40 }, "pp" } }
    };
    auto result = activity_helpers::strictestMatchingActivity(m.begin(), m.end(), [](const auto& item) { return item.second; });
    Activity expectation{ 300000, 1, "LHC22a", "spass", "qc", { 1, 40 }, "pp" };
    CHECK(result == expectation);
  }
  {
    // providing a vector (default accessor) + different run numbers and validities
    std::vector<Activity> m{
      { 300000, 1, "LHC22a", "spass", "qc", { 1, 10 }, "pp" },
      { 300001, 1, "LHC22a", "spass", "qc", { 20, 30 }, "pp" }
    };
    auto result = activity_helpers::strictestMatchingActivity(m.begin(), m.end());
    Activity expectation{ 0, 1, "LHC22a", "spass", "qc", { 1, 30 }, "pp" };
    CHECK(result == expectation);
  }
  {
    // providing a vector (custom accessor) + different everything
    std::vector<Activity> m{
      { 300000, 1, "LHC22a", "spass", "qc", { 1, 10 }, "pp" },
      { 300001, 2, "LHC22b", "apass2", "qc_mc", { 20, 30 }, "PbPb" }
    };
    auto result = activity_helpers::strictestMatchingActivity(m.begin(), m.end(), [](const auto& a) { return a; });
    Activity expectation{ 0, 0, "", "", "qc", { 1, 30 }, "" };
    CHECK(result == expectation);
  }
}

TEST_CASE("test_overlappingActivity")
{
  // it's like strictestMatchingActivity, but validity is a union
  {
    // providing a map accessor + everything being the same except the validity
    std::map<int, Activity> m{
      { 1, { 300000, 1, "LHC22a", "spass", "qc", { 1, 40 }, "pp" } },
      { 2, { 300000, 1, "LHC22a", "spass", "qc", { 10, 20 }, "pp" } },
      { 4, { 300000, 1, "LHC22a", "spass", "qc", { 15, 30 }, "pp" } },
      { 3, { 300000, 1, "LHC22a", "spass", "qc", { 17, 40 }, "pp" } }
    };
    auto result = activity_helpers::overlappingActivity(m.begin(), m.end(), [](const auto& item) { return item.second; });
    Activity expectation{ 300000, 1, "LHC22a", "spass", "qc", { 17, 20 }, "pp" };
    CHECK(result == expectation);
  }
  {
    // providing a vector (default accessor) + different run numbers and validities
    std::vector<Activity> m{
      { 300000, 1, "LHC22a", "spass", "qc", { 1, 10 }, "pp" },
      { 300001, 1, "LHC22a", "spass", "qc", { 10, 30 }, "pp" }
    };
    auto result = activity_helpers::overlappingActivity(m.begin(), m.end());
    Activity expectation{ 0, 1, "LHC22a", "spass", "qc", { 10, 10 }, "pp" };
    CHECK(result == expectation);
  }
  {
    // providing a vector (custom accessor) + different everything
    std::vector<Activity> m{
      { 300000, 1, "LHC22a", "spass", "qc", { 1, 10 }, "pp" },
      { 300001, 2, "LHC22b", "apass2", "qc_mc", { 20, 30 }, "PbPb" }
    };
    auto result = activity_helpers::overlappingActivity(m.begin(), m.end(), [](const auto& a) { return a; });
    Activity expectation{ 0, 0, "", "", "qc", { 20, 10 }, "" }; // invalid validity
    CHECK(result == expectation);
  }
}
