package com.atlassian.bitbucket.internal.importer.dao;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.bitbucket.ao.AbstractAoDao;
import com.atlassian.bitbucket.internal.importer.repository.ImportTaskFailureType;
import com.atlassian.bitbucket.internal.importer.repository.ImportTaskState;
import com.atlassian.bitbucket.repository.Repository;
import com.google.common.collect.ImmutableMap;
import net.java.ao.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Date;
import java.util.List;
import java.util.Optional;

import static com.atlassian.bitbucket.internal.importer.dao.AoImportJob.CREATED_DATE_COLUMN;
import static com.atlassian.bitbucket.internal.importer.dao.AoImportJob.USER_ID_COLUMN;
import static com.atlassian.bitbucket.internal.importer.dao.AoRepositoryImportTask.*;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable;

@Component
public class AoImporterDao extends AbstractAoDao implements ImporterDao {

    @Autowired
    public AoImporterDao(ActiveObjects ao) {
        super(ao);
    }

    @Nonnull
    @Override
    public AoImportJob createJob(int userId) {

        return ao.create(AoImportJob.class, ImmutableMap.of(
                USER_ID_COLUMN, userId,
                CREATED_DATE_COLUMN, new Date()));
    }

    @Nonnull
    @Override
    public AoRepositoryImportTask createImportTask(long jobId,
                                                   @Nonnull String cloneUrl,
                                                   @Nonnull String externalRepoName,
                                                   @Nullable ImportTaskFailureType failureType,
                                                   int repositoryId,
                                                   @Nonnull ImportTaskState state) {
        if (state == ImportTaskState.FAILED) {
            requireNonNull(failureType, "failureType");
        }
        ImmutableMap.Builder<String, Object> taskBuilder = ImmutableMap.builder();
        taskBuilder.put(IMPORT_JOB_ID_COLUMN, jobId)
                .put(CLONE_URL_COLUMN, cloneUrl)
                .put(AoRepositoryImportTask.CREATED_DATE_COLUMN, new Date())
                .put(EXTERNAL_REPOSITORY_NAME_COLUMN, requireNonNull(externalRepoName, "externalRepoName"))
                .put(LAST_UPDATED_DATE_COLUMN, new Date())
                .put(REPOSITORY_ID_COLUMN, requireNonNull(repositoryId, "repositoryId"))
                .put(STATE_COLUMN, requireNonNull(state, "state"));
        if (failureType != null) {
            taskBuilder.put(FAILURE_TYPE_COLUMN, failureType);
        }

        return ao.create(AoRepositoryImportTask.class, taskBuilder.build());
    }

    @Nonnull
    @Override
    public Optional<AoImportJob> getJob(long jobId) {
        return ofNullable(ao.get(AoImportJob.class, jobId));
    }

    @Nonnull
    @Override
    public Optional<AoRepositoryImportTask> getImportTask(@Nonnull Repository repository) {
        return stream(ao.find(AoRepositoryImportTask.class, Query.select().where(
                REPOSITORY_ID_COLUMN + " = ?", requireNonNull(repository, "repository").getId())))
                .findFirst();
    }

    @Nonnull
    @Override
    public List<AoRepositoryImportTask> getUnfinishedTasks() {
        return asList(ao.find(AoRepositoryImportTask.class, Query.select().where(
                STATE_COLUMN + " = ? OR " + STATE_COLUMN + "= ?", ImportTaskState.QUEUED, ImportTaskState.STARTED)));
    }

    @Override
    public AoRepositoryImportTask updateImportTaskState(long taskId,
                                                        @Nonnull ImportTaskState state,
                                                        @Nullable ImportTaskFailureType failureType) {
        if (state == ImportTaskState.FAILED) {
            requireNonNull(failureType, "failureType");
        }
        AoRepositoryImportTask repositoryImportTask = ao.get(AoRepositoryImportTask.class, requireNonNull(taskId, "taskId"));
        repositoryImportTask.setLastUpdatedDate(new Date());
        repositoryImportTask.setState(requireNonNull(state, "state"));
        if (failureType != null) {
            repositoryImportTask.setFailureType(failureType);
        }
        repositoryImportTask.save();
        return repositoryImportTask;
    }
}
