package com.atlassian.stash.internal.license;

import com.atlassian.extras.api.*;
import com.atlassian.extras.api.bitbucket.BitbucketServerLicense;

import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.Date;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * A simple {@link DualLicense}.
 * <p>
 * The configured license, both in encoded and decoded forms, is accessed frequently. As a result, when the license
 * is cached in {@link LicenseServiceImpl}, that cache needs to be as efficient as possible. This class, paired with
 * {@link com.atlassian.stash.internal.hazelcast.LicenseStreamSerializer LicenseStreamSerializer}, allows the cache
 * configuration to be optimized:
 * <ul>
 *     <li>Handles both {@link LicenseServiceImpl#get()} and {@link LicenseServiceImpl#getAsString()}, allowing for
 *     a single cache to back both methods</li>
 *     <li>Can be efficiently serialized, unlike an {@code AtlassianLicense} or {@code BitbucketServerLicense}, allowing the
 *     cache to be remote rather than replicate-via-invalidation
 *     <ul>
 *         <li>Replicate-via-invalidation caches cannot be near-cached, so they incur an {@code IMap.executeOnKey},
 *         frequently involving a network request in a cluster, for every access</li>
 *         <li>Remote caches can be near-cached, allowing the license to be kept in the near-cache in object format,
 *         completely eliminating both network requests <i>and</i> serialization overhead</li>
 *     </ul>
 *     </li>
 * </ul>
 * It's worth noting that it's not possible to go from an {@code AtlassianLicense} back to its encoded form, only
 * from the encoded form to an {@code AtlassianLicense}. That's why both forms must be retained here.
 *
 * @since 3.2
 */
public class SimpleDualLicense implements DualLicense {

    private final BitbucketServerLicense decoded;
    private final String encoded;

    public SimpleDualLicense(@Nonnull BitbucketServerLicense decoded, @Nonnull String encoded) {
        this.decoded = checkNotNull(decoded, "decoded");
        this.encoded = checkNotNull(encoded, "encoded");
    }

    @Override
    public Collection<Contact> getContacts() {
        return decoded.getContacts();
    }

    @Override
    public Date getCreationDate() {
        return decoded.getCreationDate();
    }

    @Nonnull
    public BitbucketServerLicense getDecodedLicense() {
        return decoded;
    }

    @Override
    public String getDescription() {
        return decoded.getDescription();
    }

    @Nonnull
    @Override
    public String getEncodedLicense() {
        return encoded;
    }

    @Override
    public Date getExpiryDate() {
        return decoded.getExpiryDate();
    }

    @Override
    public Date getGracePeriodEndDate() {
        return decoded.getGracePeriodEndDate();
    }

    @Override
    public LicenseType getLicenseType() {
        return decoded.getLicenseType();
    }

    @Override
    public int getLicenseVersion() {
        return decoded.getLicenseVersion();
    }

    @Override
    public Date getMaintenanceExpiryDate() {
        return decoded.getMaintenanceExpiryDate();
    }

    @Override
    public int getMaximumNumberOfUsers() {
        return decoded.getMaximumNumberOfUsers();
    }

    @Override
    public int getNumberOfDaysBeforeExpiry() {
        return decoded.getNumberOfDaysBeforeExpiry();
    }

    @Override
    public int getNumberOfDaysBeforeGracePeriodExpiry() {
        return decoded.getNumberOfDaysBeforeGracePeriodExpiry();
    }

    @Override
    public int getNumberOfDaysBeforeMaintenanceExpiry() {
        return decoded.getNumberOfDaysBeforeMaintenanceExpiry();
    }

    @Override
    public Organisation getOrganisation() {
        return decoded.getOrganisation();
    }

    @Override
    public Partner getPartner() {
        return decoded.getPartner();
    }

    @Override
    public Product getProduct() {
        return decoded.getProduct();
    }

    @Override
    public Iterable<Product> getProducts() {
        return decoded.getProducts();
    }

    @Override
    public String getProperty(String name) {
        return decoded.getProperty(name);
    }

    @Override
    public Date getPurchaseDate() {
        return decoded.getPurchaseDate();
    }

    @Override
    public String getServerId() {
        return decoded.getServerId();
    }

    @Override
    public String getSupportEntitlementNumber() {
        return decoded.getSupportEntitlementNumber();
    }

    @Override
    public boolean isClusteringEnabled() {
        return decoded.isClusteringEnabled();
    }

    @Override
    public boolean isEvaluation() {
        return decoded.isEvaluation();
    }

    @Override
    public boolean isExpired() {
        return decoded.isExpired();
    }

    @Override
    public boolean isGracePeriodExpired() {
        return decoded.isGracePeriodExpired();
    }

    @Override
    public boolean isMaintenanceExpired() {
        return decoded.isMaintenanceExpired();
    }

    @Override
    public boolean isSubscription() {
        return decoded.isSubscription();
    }

    @Override
    public boolean isUnlimitedNumberOfUsers() {
        return decoded.isUnlimitedNumberOfUsers();
    }

    @Override
    public boolean isWithinGracePeriod() {
        return decoded.isWithinGracePeriod();
    }
}
