/*
 * 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.sidecar.common.request.data;

import java.math.BigInteger;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.cassandra.sidecar.common.utils.Preconditions;

import static org.apache.cassandra.sidecar.common.data.RestoreJobConstants.BUCKET_ID;
import static org.apache.cassandra.sidecar.common.data.RestoreJobConstants.SLICE_CHECKSUM;
import static org.apache.cassandra.sidecar.common.data.RestoreJobConstants.SLICE_COMPRESSED_SIZE;
import static org.apache.cassandra.sidecar.common.data.RestoreJobConstants.SLICE_END_TOKEN;
import static org.apache.cassandra.sidecar.common.data.RestoreJobConstants.SLICE_ID;
import static org.apache.cassandra.sidecar.common.data.RestoreJobConstants.SLICE_START_TOKEN;
import static org.apache.cassandra.sidecar.common.data.RestoreJobConstants.SLICE_STORAGE_BUCKET;
import static org.apache.cassandra.sidecar.common.data.RestoreJobConstants.SLICE_STORAGE_KEY;
import static org.apache.cassandra.sidecar.common.data.RestoreJobConstants.SLICE_UNCOMPRESSED_SIZE;

/**
 * Request payload for creating a slice.
 * A slice is a blob object that contains SSTables to be imported into Cassandra.
 * The data covers a sub-range in the ring.
 */
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CreateSliceRequestPayload
{
    private final String sliceId;
    private final int bucketId;
    private final String bucket;
    private final String key;
    private final String checksum;
    private final BigInteger firstToken; // inclusive
    private final BigInteger endToken; // inclusive
    // -- Optional fields - using Objects
    private final Long uncompressedSize;
    private final Long compressedSize;
    // ----

    @JsonCreator
    public CreateSliceRequestPayload(@JsonProperty(SLICE_ID) String sliceId,
                                     @JsonProperty(BUCKET_ID) int bucketId,
                                     @JsonProperty(SLICE_STORAGE_BUCKET) String bucket,
                                     @JsonProperty(SLICE_STORAGE_KEY) String key,
                                     @JsonProperty(SLICE_CHECKSUM) String checksum,
                                     @JsonProperty(SLICE_START_TOKEN) BigInteger firstToken,
                                     @JsonProperty(SLICE_END_TOKEN) BigInteger endToken,
                                     @JsonProperty(SLICE_UNCOMPRESSED_SIZE) Long uncompressedSize,
                                     @JsonProperty(SLICE_COMPRESSED_SIZE) Long compressedSize)
    {
        Preconditions.checkArgument(sliceId != null
                                    && checksum != null
                                    && bucket != null
                                    && key != null
                                    && firstToken != null
                                    && endToken != null,
                                    "Invalid create slice request payload");
        Preconditions.checkArgument(bucketId < Short.MAX_VALUE && bucketId >= 0,
                                    "Invalid bucketId. Valid range: [0, " + Short.MAX_VALUE + "), " +
                                    "but got " + bucketId);
        this.sliceId = sliceId;
        this.bucketId = bucketId;
        this.bucket = bucket;
        this.key = key;
        this.checksum = checksum;
        this.firstToken = firstToken;
        this.endToken = endToken;
        this.uncompressedSize = uncompressedSize;
        this.compressedSize = compressedSize;
    }

    /**
     * @return slice id to identify a slice uniquely.
     */
    @JsonProperty(SLICE_ID)
    public String sliceId()
    {
        return sliceId;
    }

    /**
     * @return bucket id to identify a bucket uniquely.
     */
    @JsonProperty(BUCKET_ID)
    public int bucketId()
    {
        return bucketId;
    }

    public short bucketIdAsShort()
    {
        return (short) bucketId;
    }

    /**
     * @return upload bucket used for slice upload
     */
    @JsonProperty(SLICE_STORAGE_BUCKET)
    public String bucket()
    {
        return bucket;
    }

    /**
     * @return upload key used for slice upload
     */
    @JsonProperty(SLICE_STORAGE_KEY)
    public String key()
    {
        return key;
    }

    /**
     * @return first token of slice; inclusive
     */
    @JsonProperty(SLICE_START_TOKEN)
    public BigInteger firstToken()
    {
        return firstToken;
    }

    /**
     * @return end token of slice; inclusive
     */
    @JsonProperty(SLICE_END_TOKEN)
    public BigInteger endToken()
    {
        return endToken;
    }

    /**
     * @return checksum of a slice
     */
    @JsonProperty(SLICE_CHECKSUM)
    public String checksum()
    {
        return checksum;
    }

    /**
     * @return the size of all files in the slice after unzipping, or null if not defined
     */
    @JsonProperty(SLICE_UNCOMPRESSED_SIZE)
    public Long uncompressedSize()
    {
        return uncompressedSize;
    }

    /**
     * @return the size of all files in the slice after unzipping, or 0 if not defined
     */
    public long uncompressedSizeOrZero()
    {
        return uncompressedSize == null ? 0 : uncompressedSize;
    }

    /**
     * @return the size of the slice, or null if not defined
     */
    @JsonProperty(SLICE_COMPRESSED_SIZE)
    public Long compressedSize()
    {
        return compressedSize;
    }

    /**
     * @return the size of the slice, or 0 if not defined
     */
    public long compressedSizeOrZero()
    {
        return compressedSize == null ? 0 : compressedSize;
    }

    /**
     * {@inheritDoc}
     */
    public String toString()
    {
        return "CreateSliceRequest{" + SLICE_ID + "='" + sliceId + "', " +
               BUCKET_ID + "='" + bucketId + "', " +
               SLICE_STORAGE_BUCKET + "='" + bucket + "', " +
               SLICE_STORAGE_KEY + "='" + key + "', " +
               SLICE_CHECKSUM + "='" + checksum + "', " +
               SLICE_START_TOKEN + "='" + firstToken + "', " +
               SLICE_END_TOKEN + "='" + endToken + "', " +
               SLICE_COMPRESSED_SIZE + "='" + compressedSize + "', " +
               SLICE_UNCOMPRESSED_SIZE + "='" + uncompressedSize + "'}";
    }
}
