package com.atlassian.bitbucket.internal.mirroring.mirror.rest.webhook;

import com.atlassian.bitbucket.internal.mirroring.mirror.rest.RestUnknownEntity;
import com.atlassian.bitbucket.internal.mirroring.mirror.rest.webhook.cloud.*;
import com.atlassian.bitbucket.internal.mirroring.mirror.rest.webhook.server.*;
import com.atlassian.bitbucket.mirroring.mirror.UpstreamServerType;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;
import java.util.Map;
import java.util.Optional;

import static com.atlassian.bitbucket.internal.mirroring.mirror.rest.webhook.cloud.RestCloudWebHookPropertyNames.DATA;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Optional.empty;
import static org.slf4j.LoggerFactory.getLogger;

@Component
public class DefaultRestWebHookEventConverter implements RestWebHookEventConverter {

    private static final Logger log = getLogger(DefaultRestWebHookEventConverter.class);

    @Nonnull
    @Override
    public Optional<? extends BaseRestWebHookEvent> convert(@Nonnull UpstreamServerType upstreamType, @Nonnull RestUnknownEntity unknownEntity) {
        checkNotNull(upstreamType, "upstreamType");
        checkNotNull(unknownEntity, "unknownEntity");

        switch(upstreamType) {
            case BITBUCKET_CLOUD:
                return convertToCloudWebHookEvent(unknownEntity);
            case BITBUCKET_SERVER:
                return convertToServerWebHookEvent(unknownEntity);
            default:
                log.warn("Unexpected upstream server type {} encountered while processing web hook", upstreamType);
                return empty();
        }
    }

    private Optional<? extends BaseRestWebHookEvent> convertToCloudWebHookEvent(RestUnknownEntity unknownEntity) {
        Optional<CloudWebHookEventId> eventId = CloudWebHookEventId.fromEntity(unknownEntity);
        if (!eventId.isPresent()) {
            log.debug("Unable to determine type of cloud web hook due to a missing key - dropping web hook payload");
            return empty();
        }

        Optional<BaseRestCloudWebHookEvent> event = eventId.map(id -> toCloudEvent(id, unknownEntity));
        if (!event.isPresent()) {
            log.debug("Unexpected cloud web hook key {} - dropping web hook payload", eventId);
            return empty();
        }

        return event;
    }

    private Optional<? extends BaseRestWebHookEvent> convertToServerWebHookEvent(@Nonnull RestUnknownEntity unknownEntity) {
        Optional<ServerWebHookEventId> eventId = ServerWebHookEventId.fromEntity(unknownEntity);
        if (!eventId.isPresent()) {
            log.debug("Unable to determine type of server web hook due to a missing key - dropping");
            return empty();
        }

        Optional<BaseRestServerWebHookEvent> event = eventId.map(id -> toServerEvent(id, unknownEntity));
        if (!event.isPresent()) {
            log.debug("Unexpected server web hook key {} - dropping web hook payload", eventId);
            return empty();
        }

        return event;
    }

    @SuppressWarnings("unchecked")
    private BaseRestCloudWebHookEvent toCloudEvent(CloudWebHookEventId eventId, RestUnknownEntity unknownEntity) {
        Object data = unknownEntity.get(DATA);

        if (data == null || !(data instanceof Map<?, ?>)) {
            return null;
        }

        switch (eventId) {
            case PROJECT_UPDATED:
                return new RestCloudProjectUpdatedWebHookEvent((Map<String, ?>) data);
            case REPOSITORY_CREATED:
                return new RestCloudRepositoryCreatedWebHookEvent((Map<String, ?>) data);
            case REPOSITORY_DELETED:
                return new RestCloudRepositoryDeletedWebHookEvent((Map<String, ?>) data);
            case REPOSITORY_IMPORTED:
                return new RestCloudRepositoryCreatedWebHookEvent((Map<String, ?>) data);
            case REPOSITORY_PUSH:
                return new RestCloudRepositoryPushWebHookEvent((Map<String, ?>) data);
            case REPOSITORY_UPDATED:
                return new RestCloudRepositoryUpdatedWebHookEvent((Map<String, ?>) data);
            default:
                return null;
        }
    }

    public BaseRestServerWebHookEvent toServerEvent(ServerWebHookEventId eventId, RestUnknownEntity unknownEntity) {
        switch (eventId) {
            case REPOSITORY_CREATED:
                return new RestServerRepositoryCreatedWebHookEvent(unknownEntity);
            case REPOSITORY_REFS_CHANGED:
                return new RestServerRepositoryRefsChangedWebHookEvent(unknownEntity);
            case REPOSITORY_MODIFIED:
                return new RestServerRepositoryModifiedWebHookEvent(unknownEntity);
            case REPOSITORY_DELETED:
                return new RestServerRepositoryDeletedWebHookEvent(unknownEntity);
            case PROJECT_MODIFIED:
                return new RestServerProjectModifiedWebHookEvent(unknownEntity);
            case PROJECT_CREATED:
                return new RestServerProjectCreatedWebHookEvent(unknownEntity);
            case PROJECT_DELETED:
                return new RestServerProjectDeletedWebHookEvent(unknownEntity);
            default:
                return null;
        }
    }
}
