/*
 * 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.skywalking.oap.server.core.storage.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.apache.skywalking.oap.server.core.analysis.record.Record;

/**
 * BanyanDB annotation is a holder including all annotations for BanyanDB storage
 *
 * @since 9.1.0
 */
public @interface BanyanDB {
    /**
     * GlobalIndex declares advanced global index, which are only available in BanyanDB.
     * <p>
     * Global index should only be considered if a column value has a huge value candidates, but we will need a direct
     * equal
     * query without timestamp.
     * The typical global index is designed for huge candidate of indexed values, such as `trace ID` or `segment ID`.
     * <p>
     * Only work with {@link Column}
     */
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface GlobalIndex {
    }

    /**
     * Series key is used to group time series data per metric of one entity in one place.
     * <p>
     * For example,
     * ServiceA's traffic gauge, service call per minute, includes following timestamp values, then it should be sharded
     * by service ID
     * [ServiceA(encoded ID): 01-28 18:30 values-1, 01-28 18:31 values-2, 01-28 18:32 values-3, 01-28 18:32 values-4]
     * <p>
     * BanyanDB is the 1st storage implementation supporting this. It would make continuous time series metrics stored
     * closely and compressed better.
     * <p>
     * 1. One entity could have multiple sharding keys
     * 2. If no column is appointed for this, {@link org.apache.skywalking.oap.server.core.storage.StorageData#id}
     * would be used by the storage implementation accordingly.
     * <p>
     * NOTICE, this sharding concept is NOT just for splitting data into different database instances or physical
     * files.
     * <p>
     * Only work with {@link Column}
     *
     * @since 9.3.0 Rename as SeriesID.
     * @since 9.1.0 created as a new annotation.
     * @since 9.0.0 added in {@link Column}
     */
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface SeriesID {
        /**
         * Relative entity tag
         * <p>
         * The index number determines the order of the column placed in the SeriesID.
         * BanyanDB SeriesID searching procedure uses a prefix-scanning strategy.
         * Searching series against a prefix could improve the performance.
         * <p>
         * For example, the ServiceTraffic composite "layer" and "name" as the SeriesID,
         * considering OAP finds services by "layer", the "layer" 's index should be 0 to
         * trigger a prefix-scanning.
         *
         * @return non-negative if this column be used for sharding. -1 means not as a sharding key
         */
        int index() default -1;
    }

    /**
     * Force disabling indexing declare through {@link Column}.
     * In BanyanDB, {@link GlobalIndex} provides the high performance capability to filter trace/log from super large
     * dataset, some additional conditions could be done in server memory, no indexing required in this case.
     *
     * @since 9.1.0
     */
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface NoIndexing {

    }

    /**
     * Additional information for constructing Index in BanyanDB.
     *
     * @since 9.3.0
     */
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface IndexRule {
        /**
         * IndexRule supports selecting two distinct kinds of index structures.
         */
        IndexType indexType() default IndexType.INVERTED;

        enum IndexType {
            /**
             * The `INVERTED` index is the primary option when users set up an index rule.
             * It's suitable for most tag indexing due to a better memory usage ratio and query performance.
             */
            INVERTED,
            /**
             * The `TREE` index could be better when there are high cardinalities, such as the `ID` tag and numeric duration tag.
             * In these cases, it saves much memory space.
             */
            TREE;
        }
    }

    /**
     * timestampColumn is to identify which column in {@link Record} is providing the timestamp(millisecond) for BanyanDB.
     * BanyanDB stream requires a timestamp in milliseconds.
     *
     * @since 9.3.0
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @interface TimestampColumn {
        String value();
    }

    /**
     * MeasureField defines a column as a measure's field.
     * <p>
     * Annotated: the column is a measure field.
     * Unannotated: the column is a measure tag.
     * storageOnly=true: the column is a measure tag that is not indexed.
     * storageOnly=false: the column is a measure tag that is indexed.
     * indexOnly=true: the column is a measure tag that is indexed, but not stored.
     * indexOnly=false: the column is a measure tag that is indexed and stored.
     *
     * @since 9.4.0
     */
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface MeasureField {
    }

    /**
     * StoreIDTag indicates a metric store its ID as a tag for searching.
     *
     * @since 9.4.0
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @interface StoreIDAsTag {
    }

    /**
     * Generate a TopN Aggregation and use the annotated column as a groupBy tag.
     * It also contains parameters for TopNAggregation
     *
     * @since 9.4.0
     */
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @interface TopNAggregation {
        /**
         * The size of LRU determines the maximally tolerated time range.
         * The buffers in the time range are kept in the memory so that
         * the data in [T - lruSize * n, T] would be accepted in the pre-aggregation process.
         * T = the current time in the current dimensionality.
         * n = interval in the current dimensionality.
         */
        int lruSize() default 2;

        /**
         * The max size of entries in a time window for the pre-aggregation.
         */
        int countersNumber() default 1000;
    }
}
