/**
 * This file is part of the CernVM File System.
 */

#include <gtest/gtest.h>

#include <string>

#include "repository_tag.h"

class T_RepositoryTag : public ::testing::Test {};

TEST_F(T_RepositoryTag, DefaultConstructor) {
  RepositoryTag tag;
  EXPECT_EQ("", tag.name());
  EXPECT_EQ("", tag.description());
  EXPECT_EQ("", tag.delete_tags());
  EXPECT_EQ(0, tag.auto_tag_threshold());
}

TEST_F(T_RepositoryTag, SetDeleteTags) {
  RepositoryTag tag("tag1", "desc");
  EXPECT_EQ("", tag.delete_tags());

  tag.SetDeleteTags("old_tag1 old_tag2");
  EXPECT_EQ("old_tag1 old_tag2", tag.delete_tags());

  // Other fields are independent of the delete list
  EXPECT_EQ("tag1", tag.name());
  EXPECT_EQ("desc", tag.description());
}

TEST_F(T_RepositoryTag, NameDescriptionConstructor) {
  RepositoryTag tag("my_tag", "my description");
  EXPECT_EQ("my_tag", tag.name());
  EXPECT_EQ("my description", tag.description());
  EXPECT_EQ(0, tag.auto_tag_threshold());
}

TEST_F(T_RepositoryTag, SetAutoTagThreshold) {
  RepositoryTag tag("tag1", "desc");
  EXPECT_EQ(0, tag.auto_tag_threshold());

  tag.SetAutoTagThreshold(1700000000);
  EXPECT_EQ(1700000000, tag.auto_tag_threshold());

  tag.SetAutoTagThreshold(1234567890);
  EXPECT_EQ(1234567890, tag.auto_tag_threshold());

  // Setting 0 disables the cleanup
  tag.SetAutoTagThreshold(0);
  EXPECT_EQ(0, tag.auto_tag_threshold());
}

TEST_F(T_RepositoryTag, HasGenericName) {
  RepositoryTag tag1("generic-2024-01-01T00:00:00.000Z", "");
  EXPECT_TRUE(tag1.HasGenericName());

  RepositoryTag tag2("my-custom-tag", "");
  EXPECT_FALSE(tag2.HasGenericName());

  RepositoryTag tag3("generic_1-2024-01-01T00:00:00.000Z", "");
  // generic_ prefix should NOT be matched by HasGenericName
  // (it only checks for "generic-" prefix)
  EXPECT_FALSE(tag3.HasGenericName());
}

TEST_F(T_RepositoryTag, IsAutoGeneratedName) {
  // Real auto-tag formats produced by SetGenericName() and by
  // cvmfs_server_publish.sh must be recognised.
  EXPECT_TRUE(RepositoryTag::IsAutoGeneratedName(
      "generic-2024-01-01T00:00:00.000Z"));
  EXPECT_TRUE(RepositoryTag::IsAutoGeneratedName(
      "generic-2024-01-01T00:00:00Z"));
  EXPECT_TRUE(RepositoryTag::IsAutoGeneratedName(
      "generic_1-2024-01-01T00:00:00Z"));
  EXPECT_TRUE(RepositoryTag::IsAutoGeneratedName(
      "generic_42-2026-06-16T12:00:00Z"));

  // User-created tags must NOT be matched, even with a "generic" prefix, so
  // the cleanup never deletes them.
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName("generic-release"));
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName("generic-release-candidate"));
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName("generic_release-2024"));
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName("my-custom-tag"));
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName("trunk"));

  // Fractional seconds of 1-3 digits are accepted.
  EXPECT_TRUE(RepositoryTag::IsAutoGeneratedName(
      "generic-2024-01-01T00:00:00.5Z"));
  EXPECT_TRUE(RepositoryTag::IsAutoGeneratedName(
      "generic-2024-01-01T00:00:00.50Z"));

  // Malformed / boundary cases. The "<ISO8601>" portion must be a *complete*
  // YYYY-MM-DDTHH:MM:SS(.mmm)?Z timestamp, matching filter_auto_tags().
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName(""));
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName("generic-"));
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName("generic_"));
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName(
      "generic_-2024-01-01T00:00:00Z"));    // empty numeric suffix
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName(
      "generic-2024-Z"));                    // year only, not a full timestamp
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName(
      "generic-2024-01-01T00:00:00.000"));   // missing trailing 'Z'
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName(
      "generic-202X-01-01T00:00:00Z"));      // non-digit year
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName(
      "generic-2024-01-01 00:00:00Z"));      // space instead of 'T'
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName(
      "generic-2024-01-01T00:00:00.0000Z"));  // too many fractional digits
  EXPECT_FALSE(RepositoryTag::IsAutoGeneratedName(
      "generic-2024-01-01T00:00:00ZX"));      // trailing garbage after 'Z'
}

TEST_F(T_RepositoryTag, SetGenericName) {
  RepositoryTag tag;
  tag.SetName("generic-placeholder");
  EXPECT_TRUE(tag.HasGenericName());

  tag.SetGenericName();
  // After SetGenericName, the tag should still have a generic name
  // but with a real timestamp
  EXPECT_TRUE(tag.HasGenericName());
  // Verify the format: generic-YYYY-MM-DDThh:mm:ss.mmmZ
  EXPECT_EQ(0u, tag.name().find("generic-"));
  // A freshly generated name must round-trip through the cleanup matcher, so
  // that auto tags created today can later be pruned.
  EXPECT_TRUE(RepositoryTag::IsAutoGeneratedName(tag.name()));
}

TEST_F(T_RepositoryTag, AutoTagThresholdPreservedAcrossMutations) {
  RepositoryTag tag("tag1", "desc");
  tag.SetAutoTagThreshold(1700000000);

  tag.SetName("tag2");
  EXPECT_EQ(1700000000, tag.auto_tag_threshold());

  tag.SetDescription("new desc");
  EXPECT_EQ(1700000000, tag.auto_tag_threshold());
}
