/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.cassandra.bridge;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;

/*
 * An enum that describes all possible Cassandra versions that can potentially be supported, even if the bridge is not yet implemented.
 * Customers of this library looking to implement additional bridges or replace existing ones with proprietary implementations
 * should inject/replace bridge implementation JARs embedded into this library's resources and replace this class with an identical one,
 * but with implementedVersions() and supportedVersions() modified accordingly.
 *
 * NOTE: The following values need to stay in sync with:
 * - build.gradle:
 *   - ext.cassandraVersionEnumMap = ["4.0": "FOURZERO", "4.1": "FOURONE", "5.0": "FIVEZERO"]
 *   - ext.cassandraFullVersionMap = ["4.0": "4.0.17", "4.1": "4.1.4", "5.0": "5.0.5"]
 * - build-dtest-jars.sh:
 *   - CANDIDATE_BRANCHES=(
 *      "cassandra-4.0:cassandra-4.0.17"
 *      "cassandra-4.1:99d9faeef57c9cf5240d11eac9db5b283e45a4f9"
 *      "cassandra-5.0:cassandra-5.0.5"
 */
public enum CassandraVersion
{
    THREEZERO(30, "3.0", "three-zero", "big"),
    FOURZERO(40, "4.0", "four-zero", "big"),
    FOURONE(41, "4.1", "four-zero", "big"),
    FIVEZERO(50, "5.0", "five-zero", "big", "bti");

    private final int number;
    private final String name;
    private final String jarBaseName;  // Must match shadowJar.archiveFileName from Gradle configuration (without extension)
    private final Set<String> sstableFormats;

    CassandraVersion(int number, String name, String jarBaseName, String... sstableFormats)
    {
        this.number = number;
        this.name = name;
        this.jarBaseName = jarBaseName;
        this.sstableFormats = new HashSet<>(Arrays.asList(sstableFormats));
    }

    public int versionNumber()
    {
        return number;
    }

    public String versionName()
    {
        return name;
    }

    public String jarBaseName()
    {
        return jarBaseName;
    }

    private static final String sstableFormat;
    private static final CassandraVersion[] implementedVersions;
    private static final String[] supportedVersions;

    static
    {
        sstableFormat = System.getProperty("cassandra.analytics.bridges.sstable_format", "big");

        // NOTE: These default enum names must stay in sync with cassandraVersionEnumMap in build.gradle.
        // FOURONE is intentionally excluded from local-dev defaults to keep iteration fast;
        // CI covers 4.1 via explicit CASSANDRA_VERSION env var or per-version Gradle tasks (e.g. testCassandra41).
        String providedVersionsOrDefault = System.getProperty("cassandra.analytics.bridges.implemented_versions",
                                                              String.join(",", FOURZERO.name(), FIVEZERO.name()));
        implementedVersions = Arrays.stream(providedVersionsOrDefault.split(","))
                                    .map(CassandraVersion::valueOf)
                                    .filter(v -> v.sstableFormats.contains(sstableFormat))
                                    .toArray(CassandraVersion[]::new);

        // NOTE: These default versions must stay in sync with cassandraFullVersionMap in build.gradle.
        String providedSupportedVersionsOrDefault = System.getProperty("cassandra.analytics.bridges.supported_versions",
                                                                       "cassandra-4.0.17,cassandra-5.0.5");
        supportedVersions = Arrays.stream(providedSupportedVersionsOrDefault.split(","))
                                  .filter(version -> CassandraVersion.fromVersion(version)
                                                                     .filter(v -> v.sstableFormats.contains(sstableFormat))
                                                                     .isPresent())
                                  .toArray(String[]::new);

        Preconditions.checkArgument(implementedVersions.length > 0 && supportedVersions.length > 0,
                                    "No versions available");
    }

    public static String sstableFormat()
    {
        return sstableFormat;
    }

    public static Optional<CassandraVersion> fromVersion(String cassandraVersion)
    {
        CassandraVersionFeatures features = CassandraVersionFeatures.cassandraVersionFeaturesFromCassandraVersion(cassandraVersion);
        return Arrays.stream(CassandraVersion.values())
                     .filter(value -> value.versionNumber() == features.getMajorVersion())
                     .findAny();
    }

    public static CassandraVersion[] implementedVersions()
    {
        return implementedVersions;
    }

    @VisibleForTesting
    public static String[] supportedVersions()
    {
        return supportedVersions;
    }
}
