package com.atlassian.stash.internal.build;

import com.atlassian.bitbucket.build.*;
import com.atlassian.event.api.EventPublisher;
import com.atlassian.sal.api.transaction.TransactionCallback;
import com.atlassian.sal.api.transaction.TransactionTemplate;
import com.atlassian.stash.internal.build.dao.AoBuildStatus;
import com.atlassian.stash.internal.build.dao.BuildStatusDao;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.permission.PermissionValidationService;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;

import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;

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

public class DefaultBuildStatusService implements BuildStatusService {

    static final String PROPERTY_MAX_BUILD_STATUSES = "plugin.bitbucket-build.max.statuses";

    private static final Function<AoBuildStatus, BuildStatus> TRANSFORM = status ->
            new InternalBuildStatus(status.getState(), status.getKey(), status.getName(), status.getUrl(),
                    status.getDescription(), status.getDateAdded());

    private final BuildStatusDao buildStatusDao;
    private final EventPublisher eventPublisher;
    private final PermissionValidationService permissionValidationService;
    private final TransactionTemplate transactionTemplate;
    private final int maxStatuses;

    public DefaultBuildStatusService(BuildStatusDao buildStatusDao, EventPublisher eventPublisher,
                                     PermissionValidationService permissionValidationService,
                                     ApplicationPropertiesService propertiesService,
                                     TransactionTemplate transactionTemplate) {
        this.buildStatusDao = buildStatusDao;
        this.eventPublisher = eventPublisher;
        this.permissionValidationService = permissionValidationService;
        this.transactionTemplate = transactionTemplate;

        maxStatuses = propertiesService.getPluginProperty(PROPERTY_MAX_BUILD_STATUSES, 100);
    }

    @Nonnull
    @Override
    public Page<BuildStatus> findAll(@Nonnull String commitId) {
        validateUser();

        checkNotNull(commitId, "commitId");

        return buildStatusDao.findAll(commitId, PageUtils.newRequest(0, maxStatuses))
                .transform(TRANSFORM);
    }

    @Nonnull
    @Override
    public BuildSummary getSummary(@Nonnull String commitId) {
        validateUser();

        checkNotNull(commitId, "commitId");

        return new SimpleBuildSummary(buildStatusDao.countByState(commitId));
    }

    @Nonnull
    @Override
    public Map<String, BuildSummary> getSummaries(@Nonnull Collection<String> commitIds) {
        validateUser();

        if (checkNotNull(commitIds, "commitIds").isEmpty()) {
            return Collections.emptyMap();
        }

        return ImmutableMap.copyOf(Maps.transformValues(buildStatusDao.countByState(commitIds),
                statuses -> new SimpleBuildSummary(statuses)));
    }

    @Override
    public void set(@Nonnull final BuildStatusSetRequest request) {
        validateUser();

        checkNotNull(request, "request");

        BuildStatus status = transactionTemplate.execute(new TransactionCallback<BuildStatus>() {

            @Override
            public BuildStatus doInTransaction() {
                return buildStatusDao.set(request);
            }
        });
        eventPublisher.publish(new BuildStatusSetEvent(this, request.getCommitId(), status));
    }

    private void validateUser() {
        permissionValidationService.validateForGlobal(Permission.LICENSED_USER);
    }
}
