package io.cattle.platform.iaas.api.auth.projects;

import io.cattle.platform.api.auth.Identity;
import io.cattle.platform.api.auth.Policy;
import io.cattle.platform.api.resource.AbstractObjectResourceManager;
import io.cattle.platform.core.constants.ProjectConstants;
import io.cattle.platform.core.dao.GenericResourceDao;
import io.cattle.platform.core.model.Account;
import io.cattle.platform.core.model.ProjectMember;
import io.cattle.platform.iaas.api.auth.dao.AuthDao;
import io.cattle.platform.iaas.api.auth.identity.IdentityManager;
import io.cattle.platform.json.JsonMapper;
import io.cattle.platform.object.ObjectManager;
import io.cattle.platform.object.process.ObjectProcessManager;
import io.github.ibuildthecloud.gdapi.context.ApiContext;
import io.github.ibuildthecloud.gdapi.exception.ClientVisibleException;
import io.github.ibuildthecloud.gdapi.factory.SchemaFactory;
import io.github.ibuildthecloud.gdapi.model.ListOptions;
import io.github.ibuildthecloud.gdapi.request.ApiRequest;
import io.github.ibuildthecloud.gdapi.util.RequestUtils;
import io.github.ibuildthecloud.gdapi.util.ResponseCodes;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;

public class ProjectMemberResourceManager extends AbstractObjectResourceManager {

    @Inject
    JsonMapper jsonMapper;
    @Inject
    AuthDao authDao;
    @Inject
    GenericResourceDao resourceDao;
    @Inject
    ObjectManager objectManager;
    @Inject
    ObjectProcessManager objectProcessManager;
    @Inject
    IdentityManager identityManager;

    @Override
    protected Object removeFromStore(String type, String id, Object obj, ApiRequest request) {
        throw new UnsupportedOperationException();
    }

    @Override
    protected Object listInternal(SchemaFactory schemaFactory, String type, Map<Object, Object> criteria, ListOptions options) {
        Policy policy = (Policy) ApiContext.getContext().getPolicy();
        String id = RequestUtils.makeSingularStringIfCan(criteria.get("id"));
        if (StringUtils.isNotEmpty(id)) {
            ProjectMember projectMember;
            try {
                 projectMember = authDao.getProjectMember(Long.valueOf(id));
            } catch (NumberFormatException e) {
                throw new ClientVisibleException(ResponseCodes.NOT_FOUND);
            }
            if (projectMember == null) {
                throw new ClientVisibleException(ResponseCodes.NOT_FOUND);
            }
            if (!authDao.hasAccessToProject(projectMember.getProjectId(), policy.getAccountId(),
                    policy.isOption(Policy.AUTHORIZED_FOR_ALL_ACCOUNTS), policy.getIdentities())) {
                throw new ClientVisibleException(ResponseCodes.NOT_FOUND);
            }
            Identity identity = identityManager.projectMemberToIdentity(projectMember);
            policy.grantObjectAccess(identity);
            return Collections.singletonList(identity);

        }
        String projectId = RequestUtils.makeSingularStringIfCan(criteria.get("projectId"));
        List<? extends ProjectMember> members;
        if (StringUtils.isNotEmpty(projectId)) {
            members = authDao.getActiveProjectMembers(Long.valueOf(projectId));
        } else {
            members = authDao.getActiveProjectMembers(policy.getAccountId());
        }
        List<Identity> identities = new ArrayList<>();
        for (ProjectMember member:members){
            Identity identity = identityManager.projectMemberToIdentity(member);
            identities.add(identity);
            policy.grantObjectAccess(identity);
        }
        return identities;
    }

    @Override
    public String[] getTypes() {
        return new String[]{"projectMember"};
    }

    @Override
    public Class<?>[] getTypeClasses() {
        return new Class<?>[0];
    }

    @Override
    protected Object createInternal(String type, ApiRequest request) {
        throw new ClientVisibleException(ResponseCodes.METHOD_NOT_ALLOWED);
    }

    public List<ProjectMember> setMembers(Account project, List<Map<String, String>> members, boolean forProjectCreate) {
        List<ProjectMember> membersCreated = new ArrayList<>();
        Set<Member> membersTransformed = new HashSet<>();

        if ((members == null || members.isEmpty())) {
            Policy policy = (Policy) ApiContext.getContext().getPolicy();
            Identity idToUse = null;
            for (Identity identity : policy.getIdentities()) {
                if (idToUse == null) {
                    if (identity.getExternalIdType().equalsIgnoreCase(ProjectConstants.RANCHER_ID)) {
                        idToUse = identity;
                    }
                }
            }
            if (idToUse != null) {
                Member owner = new Member(idToUse, ProjectConstants.OWNER);
                membersTransformed.add(owner);
            }

        } else {
            for (Map<String, String> newMember : members) {
                if (newMember.get("externalId") == null || newMember.get("externalIdType") == null || newMember.get("role") == null) {

                    throw new ClientVisibleException(ResponseCodes.BAD_REQUEST, "InvalidFormat", "Member format " +
                            "invalid", null);
                }
            }

            for (Map<String, String> newMember : members) {
                Identity givenIdentity = new Identity(newMember.get("externalIdType"), newMember.get("externalId"));
                givenIdentity = identityManager.projectMemberToIdentity(givenIdentity);
                membersTransformed.add(new Member(givenIdentity, newMember.get("role")));
            }
        }


        boolean hasOwner = false;
        Set<Member> newOwners = new HashSet<>();
        for (Member member : membersTransformed) {
            if (member.getRole().equalsIgnoreCase(ProjectConstants.OWNER)) {
                hasOwner = true;
                newOwners.add(member);
            }
        }

        if (!hasOwner) {
            throw new ClientVisibleException(ResponseCodes.BAD_REQUEST, "InvalidFormat", "Members list does not have an owner", null);

        }

        Policy policy = (Policy) ApiContext.getContext().getPolicy();
        boolean isOwner = authDao.isProjectOwner(project.getId(), policy.getAccountId(), policy.isOption(Policy.AUTHORIZED_FOR_ALL_ACCOUNTS), policy
                .getIdentities());
        // Can only change project owners if you're an owner or this is part of initial project create (to assign yourself as owner)
        if (!isOwner && !forProjectCreate) {
            List<? extends ProjectMember>currentMembers = authDao.getActiveProjectMembers(project.getId());
            Set<Member> currentOwners = new HashSet<>();
            String projectId = (String) ApiContext.getContext().getIdFormatter().formatId(objectManager.getType(Account.class), project.getId());
            for (ProjectMember m : currentMembers) {
                if (ProjectConstants.OWNER.equalsIgnoreCase(m.getRole())) {
                    currentOwners.add(new Member(m, projectId));
                }
            }
            if (!newOwners.equals(currentOwners)) {
                throw new ClientVisibleException(ResponseCodes.BAD_REQUEST, "InvalidFormat", "Cannot change project owners", null);
            }
        }

        membersCreated.addAll(authDao.setProjectMembers(project, membersTransformed, ApiContext.getContext()
                .getIdFormatter()));

        for (ProjectMember member : membersCreated) {
            identityManager.untransform(identityManager.projectMemberToIdentity(member), true);
        }
        return membersCreated;
    }
}
