package com.atlassian.stash.internal.repository.ref.restriction.dao.v1;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.activeobjects.external.ActiveObjectsUpgradeTask;
import com.atlassian.activeobjects.external.ModelVersion;
import com.atlassian.sal.api.transaction.TransactionCallback;
import com.atlassian.stash.internal.repository.ref.restriction.dao.v0.AoPermittedEntityV0;
import com.atlassian.stash.internal.repository.ref.restriction.dao.v0.AoRestrictedRefV0;
import com.atlassian.bitbucket.repository.ref.restriction.RefRestrictionType;
import net.java.ao.Accessor;
import net.java.ao.Mutator;
import net.java.ao.Preload;
import net.java.ao.Query;
import net.java.ao.schema.Indexed;
import net.java.ao.schema.Table;

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

public class MigrateToV1Task implements ActiveObjectsUpgradeTask {

    private static final int PAGE_SIZE = 500;

    @Override
    public ModelVersion getModelVersion() {
        // The version this upgrade task upgrades *to*
        return ModelVersion.valueOf("1");
    }

    @SuppressWarnings("unchecked")
    @Override
    public void upgrade(ModelVersion currentVersion, final ActiveObjects ao) {
        checkState(currentVersion.isSame(ModelVersion.valueOf("0")), "This task can only upgrade from version 0 to 1");

        ao.migrateDestructively(AoRefRestrictionV0ToV1.class, AoPermittedEntityV0.class);

        // Page to limit memory usage
        boolean fullPageRetrieved;
        do {
            fullPageRetrieved = ao.executeInTransaction(new TransactionCallback<Boolean>() {
                @Override
                public Boolean doInTransaction() {
                    AoRefRestrictionV0ToV1[] restrictions = ao.find(AoRefRestrictionV0ToV1.class, Query
                            .select()
                            .where(AoRefRestrictionV0ToV1.RESTRICTION_TYPE_COLUMN + " IS null ")
                            .limit(PAGE_SIZE));

                    for (AoRefRestrictionV0ToV1 restriction : restrictions) {
                        restriction.setRestrictionType(RefRestrictionType.READ_ONLY.getId());
                        restriction.save();
                    }

                    return restrictions.length == PAGE_SIZE;
                }
            });
        } while (fullPageRetrieved);
    }

    @Table("RESTRICTED_REF")
    @Preload
    public static interface AoRefRestrictionV0ToV1 extends AoRestrictedRefV0 {
        String RESTRICTION_TYPE_COLUMN = "RESTRICTION_TYPE";

        // Note: no @NotNull annotation just yet
        @Accessor(RESTRICTION_TYPE_COLUMN)
        @Indexed
        String getRestrictionType();

        @Mutator(RESTRICTION_TYPE_COLUMN)
        void setRestrictionType(String type);
    }
}
