package com.atlassian.bitbucket.ssh.event;

import com.atlassian.bitbucket.audit.AuditEntry;
import com.atlassian.bitbucket.audit.AuditEntryBuilder;
import com.atlassian.bitbucket.audit.AuditEntryConverter;
import com.atlassian.bitbucket.project.Project;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.ssh.SshAccessKey;
import com.atlassian.bitbucket.ssh.SshKey;
import com.atlassian.bitbucket.util.AuditUtils;
import com.google.common.collect.ImmutableMap;

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

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

/**
 * {@link AuditEntryConverter} for {@link SshAccessKeyEvent}s. This converter is used to
 * integrate {@link SshAccessKeyEvent}s with the auditing system.
 *
 * @since 4.3
 */
public class SshAccessKeyEventConverter implements AuditEntryConverter<SshAccessKeyEvent> {

    @Nonnull
    @Override
    public AuditEntry convert(@Nonnull SshAccessKeyEvent event, @Nonnull AuditEntryBuilder builder) {
        SshAccessKey accessKey = checkNotNull(checkNotNull(event, "event").getAccessKey(), "event.accessKey");

        Map<String, Object> keyMap = createKeyMap(accessKey.getKey());
        ImmutableMap.Builder<String, Object> detailsBuilder = ImmutableMap.<String, Object>builder()
                .put("key", keyMap)
                .put("permission", accessKey.getPermission().name());

        Project project = null;
        Repository repository = null;
        if (accessKey.getResource() instanceof Project) {
            project = (Project) accessKey.getResource();
            detailsBuilder.put("project", createProjectMap(project));
        } else {
            repository = (Repository) accessKey.getResource();
            detailsBuilder.put("repository", createRepositoryMap(repository));
        }

        Map<String, Object> details = detailsBuilder.build();
        try {
            return builder
                    .action(event.getClass())
                    .timestamp(new Date())
                    .details(AuditUtils.toJson(details))
                    .user(event.getUser())
                    .project(project)
                    .repository(repository)
                    .target(AuditUtils.toJson(keyMap))
                    .build();
        } catch (RuntimeException e) {
            throw new RuntimeException(String.format("Failed to convert map %s to JSON", details), e);
        }
    }

    private static Map<String, Object> createKeyMap(SshKey key) {
        ImmutableMap.Builder<String, Object> builder = ImmutableMap.<String, Object>builder()
                .put("id", key.getId());
        if (key.getLabel() != null) {
            builder.put("label", key.getLabel());
        }
        return builder.build();
    }

    private static Map<String, Object> createRepositoryMap(Repository repository) {
        return ImmutableMap.<String, Object>builder()
                .put("id", repository.getId())
                .put("slug", repository.getSlug())
                .put("project", createProjectMap(repository.getProject()))
                .build();
    }

    private static Map<String, Object> createProjectMap(Project project) {
        return ImmutableMap.<String, Object>builder()
                .put("id", project.getId())
                .put("key", project.getKey())
                .build();
    }
}
