package com.atlassian.bitbucket.internal.mirroring.rest.auth;

import com.atlassian.bitbucket.internal.mirroring.user.ApplicationUserWithPermissions;
import com.atlassian.bitbucket.internal.mirroring.user.ApplicationUserWithPermissionsVisitor;
import com.atlassian.bitbucket.internal.mirroring.user.SimpleApplicationUserWithPermissions;
import com.atlassian.bitbucket.permission.*;
import com.atlassian.bitbucket.rest.permission.RestEffectiveGlobalPermission;
import com.atlassian.bitbucket.rest.permission.RestEffectiveProjectPermission;
import com.atlassian.bitbucket.rest.permission.RestEffectiveRepositoryPermission;
import com.atlassian.bitbucket.rest.user.RestApplicationUser;
import com.atlassian.bitbucket.user.ApplicationUserVisitor;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import javax.annotation.Nonnull;
import java.util.Collections;

public class RestApplicationUserWithPermissions extends RestApplicationUser implements ApplicationUserWithPermissions {

    public static final Function<ApplicationUserWithPermissions, RestApplicationUserWithPermissions> REST_TRANSFORM =
            user -> user == null ? null : new RestApplicationUserWithPermissions(user);

    public static final Function<Object, EffectivePermission> TO_EFFECTIVE_PERMISSION = input -> {
        if (input instanceof EffectivePermission) {
            return (EffectivePermission) input;
        }

        EffectivePermission effectivePerm = RestEffectiveProjectPermission.valueOf(input);
        if (effectivePerm == null) {
            effectivePerm = RestEffectiveRepositoryPermission.valueOf(input);
        }
        if (effectivePerm == null) {
            effectivePerm = RestEffectiveGlobalPermission.valueOf(input);
        }
        return effectivePerm;
    };

    @SuppressWarnings("unused") //Required for Jersey
    public RestApplicationUserWithPermissions() {
    }

    public RestApplicationUserWithPermissions(ApplicationUserWithPermissions user) {
        super(user);

        put("effectivePermissions", Lists.newLinkedList(Iterables.transform(user.getEffectivePermissions(),
                permission -> permission.accept(new AbstractEffectivePermissionVisitor<Object>() {
            @Override
            public Object visit(EffectiveGlobalPermission permission) {
                return new RestEffectiveGlobalPermission(permission);
            }

            @Override
            public Object visit(EffectiveProjectPermission permission) {
                return new RestEffectiveProjectPermission(permission);
            }

            @Override
            public Object visit(EffectiveRepositoryPermission permission) {
                return new RestEffectiveRepositoryPermission(permission);
            }
        }))));
    }

    @Nonnull
    @Override
    public Iterable<EffectivePermission> getEffectivePermissions() {
        //noinspection unchecked
        Iterable<Object> effectivePermissions = (Iterable<Object>) get("effectivePermissions");
        if (effectivePermissions == null) {
            return Collections.emptyList();
        }

        return Lists.newArrayList(Iterables.filter(Iterables.transform(
                effectivePermissions, TO_EFFECTIVE_PERMISSION), Predicates.notNull()));
    }

    @Override
    public <T> T accept(@Nonnull ApplicationUserVisitor<T> visitor) {
        if (visitor instanceof ApplicationUserWithPermissionsVisitor) {
            return ((ApplicationUserWithPermissionsVisitor<T>) visitor).visit(this);
        }

        return super.accept(visitor);
    }

    public static final RestApplicationUserWithPermissions RESPONSE_EXAMPLE = new RestApplicationUserWithPermissions(
            new SimpleApplicationUserWithPermissions(RestApplicationUser.RESPONSE_EXAMPLE,
                Lists.<EffectivePermission>newArrayList(
                        new RestEffectiveGlobalPermission(new SimpleEffectiveGlobalPermission(Permission.LICENSED_USER)),
                        new RestEffectiveProjectPermission(new SimpleEffectiveProjectPermission(7, Permission.PROJECT_VIEW)),
                        new RestEffectiveRepositoryPermission(new SimpleEffectiveRepositoryPermission(6, Permission.REPO_WRITE))
                )));
}
