package com.atlassian.bitbucket.ao;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.activeobjects.external.ActiveObjectsUpgradeTask;
import com.atlassian.activeobjects.external.ModelVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

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

/**
 * Represents {@link ActiveObjectsUpgradeTask Active Objects Upgrade Task} that consists of several
 * {@link ActiveObjectsUpgradeSubtask subtasks}. Subtasks are executed sequentially in the order they were passed
 * into {@link #AbstractActiveObjectsCompositeUpgradeTask(int, ActiveObjectsUpgradeSubtask...) constructor}.
 */
public abstract class AbstractActiveObjectsCompositeUpgradeTask implements ActiveObjectsUpgradeTask {
    private static final Logger log = LoggerFactory.getLogger(AbstractActiveObjectsCompositeUpgradeTask.class);

    private final List<ActiveObjectsUpgradeSubtask> subtasks;
    private final int toModelVersion;

    protected AbstractActiveObjectsCompositeUpgradeTask(int toModelVersion, @Nonnull ActiveObjectsUpgradeSubtask... subtasks) {
        this.subtasks = Arrays.asList(Objects.requireNonNull(subtasks, "subtasks"));
        this.toModelVersion = toModelVersion;
    }

    @Override
    public ModelVersion getModelVersion() {
        return ModelVersion.valueOf(String.valueOf(toModelVersion));
    }

    @Override
    public void upgrade(ModelVersion currentVersion, ActiveObjects ao) {
        int fromModelVersion = toModelVersion - 1;
        checkState(currentVersion.isSame(ModelVersion.valueOf(String.valueOf(fromModelVersion))),
                String.format("This task can only upgrade from version %d to %d", fromModelVersion, toModelVersion));

        // any failing subtask should cause rollback of this upgrade task
        ao.executeInTransaction(() -> {
            for (ActiveObjectsUpgradeSubtask subtask : subtasks) {
                log.debug("Running upgrade subtask {}", subtask.getName());
                subtask.upgrade(ao);
            }
            return null;
        });
    }
}
