/*
 * Created by IntelliJ IDEA.
 * User: Mike
 * Date: Feb 19, 2004
 * Time: 3:03:09 PM
 */
package com.atlassian.confluence.rpc.soap.services;

import com.atlassian.confluence.core.ContentEntityObject;
import com.atlassian.confluence.links.LinkManager;
import com.atlassian.confluence.links.OutgoingLink;
import com.atlassian.confluence.pages.AbstractPage;
import com.atlassian.confluence.pages.BlogPost;
import com.atlassian.confluence.pages.Page;
import com.atlassian.confluence.pages.PageManager;
import com.atlassian.confluence.rpc.NotPermittedException;
import com.atlassian.confluence.rpc.RemoteException;
import com.atlassian.confluence.security.Permission;
import com.atlassian.confluence.security.PermissionManager;
import com.atlassian.confluence.spaces.Space;
import com.atlassian.confluence.spaces.SpaceManager;
import com.atlassian.confluence.user.AuthenticatedUserThreadLocal;
import com.atlassian.confluence.user.UserAccessor;
import com.atlassian.confluence.util.GeneralUtil;
import com.atlassian.user.User;

import java.util.Iterator;
import java.util.List;

public class SoapServiceHelperImpl implements SoapServiceHelper
{
    protected SpaceManager spaceManager;
    protected PermissionManager permissionManager;
    protected PageManager pageManager;
    protected LinkManager linkManager;
    protected UserAccessor userAccessor;

    public void setUserAccessor(UserAccessor userAccessor)
    {
        this.userAccessor = userAccessor;
    }

    public void setSpaceManager(SpaceManager spaceManager)
    {
        this.spaceManager = spaceManager;
    }

    public void setPageManager(PageManager pageManager)
    {
        this.pageManager = pageManager;
    }

    public void setLinkManager(LinkManager linkManager)
    {
        this.linkManager = linkManager;
    }

    public void setPermissionManager(PermissionManager permissionManager)
    {
        this.permissionManager = permissionManager;
    }

    /**
     * Retrieves a given space, and throws RemoteException if the space is not found or the user has no view perm.
     */
    public Space retrieveSpace(String spaceKey) throws RemoteException
    {
        User user = AuthenticatedUserThreadLocal.getUser();
        Space space = spaceManager.getSpace(spaceKey);

        if (!permissionManager.hasPermission(user, Permission.VIEW, space))
        {
            throw new RemoteException("You're not allowed to view that space, or it does not exist.");
        }

        return space;
    }

    /**
     * Retrieves a given page, and throws RemoteException if the page is not found or the user has no view perm.
     */
    public AbstractPage retrieveAbstractPage(long abstractPageId) throws RemoteException
    {
        AbstractPage page = pageManager.getAbstractPage(abstractPageId);

        assertCanView(page);
        return page;
    }

    /**
     * Retrieves a given page by space key and page title, and throws RemoteException if the page is not found or the user has no view perm.
     */
    public Page retrievePage(String spaceKey, String pageTitle) throws RemoteException
    {
        Page page = pageManager.getPage(spaceKey, pageTitle);

        assertCanView(page);
        return page;
    }

    public User retrieveUser(String username) throws RemoteException
    {
        User user = userAccessor.getUser(username);

        if (user == null)
            throw new RemoteException("The user '" + username + "' does not exist.");

        return user;
    }

    public void assertCanView(AbstractPage page) throws RemoteException
    {
        User user = AuthenticatedUserThreadLocal.getUser();
        if (page != null && GeneralUtil.isSuperUser(user))
            return;

        if (page != null && page.getOriginalVersion() != null)
            page = (AbstractPage) page.getOriginalVersion();

        if (!permissionManager.hasPermission(user, Permission.VIEW, page))
            throw new RemoteException("You're not allowed to view that page, or it does not exist.");
    }

    public void assertCanView(Space space) throws RemoteException
    {
        User user = AuthenticatedUserThreadLocal.getUser();
        if (space != null && GeneralUtil.isSuperUser(user))
            return;

        if (!permissionManager.hasPermission(user, Permission.VIEW, space))
            throw new NotPermittedException("Space does not exist, or you do not have permission to view it.");

    }

    /**
     * Renames a page, and all links to that page.
     *
     * @param page    the page to rename
     * @param newName the new name to give the page
     */
    public void renamePage(Page page, String newName) throws RemoteException
    {
        List links = linkManager.getIncomingLinksToContent(page);
        for (Iterator it = links.iterator(); it.hasNext();)
        {
            OutgoingLink link = (OutgoingLink) it.next();
            try
            {
                ContentEntityObject oldSource = (ContentEntityObject) link.getSourceContent().clone();

                String updatedContent = linkManager.getContentWithRenamedLinks(link.getSourceContent(), page, page.getSpace().getKey(), newName);
                link.getSourceContent().setContent(updatedContent);
                pageManager.saveContentEntity(link.getSourceContent(), oldSource, null);
            }
            catch (CloneNotSupportedException e)
            {
                throw new RemoteException("Something went horribly wrong renaming the links to this page", e);
            }
        }

        pageManager.renamePage(page, newName);
    }

    public void assertCanModifyObject(Object obj, String typeDescription) throws NotPermittedException
    {
        User user = AuthenticatedUserThreadLocal.getUser();

        if (GeneralUtil.isSuperUser(user))
            return;

        if (!permissionManager.hasPermission(user, Permission.EDIT, obj))
            throw new NotPermittedException("You do not have permission to edit " + typeDescription);
    }

    public void assertCanCreatePage(Space space) throws RemoteException
    {
        assertCanCreate(space, Page.class, "pages");
    }

    public void assertCanCreateBlogPost(Space space) throws RemoteException
    {
        assertCanCreate(space, BlogPost.class, "blog posts");
    }

    private void assertCanCreate(Space space, Class typeToCreate, String typeDescription) throws RemoteException
    {
        assertCanView(space);
        User user = AuthenticatedUserThreadLocal.getUser();
        if (GeneralUtil.isSuperUser(user))
            return;

        if (!permissionManager.hasCreatePermission(user, space, typeToCreate))
            throw new NotPermittedException("You do not have permission to create " + typeDescription + " in this space.");
    }

    public void assertCanModify(AbstractPage page) throws RemoteException
    {
        User user = AuthenticatedUserThreadLocal.getUser();
        if (GeneralUtil.isSuperUser(user))
            return;

        if (!permissionManager.hasPermission(user, Permission.EDIT, page))
            throw new NotPermittedException("You do not have permission to edit this page");
    }

    /**
     * Throw an exception if the user can't remove the given page.
     */

    public void assertCanRemove(AbstractPage page) throws RemoteException
    {
        User user = AuthenticatedUserThreadLocal.getUser();
        if (GeneralUtil.isSuperUser(user))
            return;

        if (!permissionManager.hasPermission(user, Permission.REMOVE, page))
            throw new NotPermittedException("You do not have permission to remove this page");
    }

    public void assertCanAdministrate() throws RemoteException
    {
        User user = AuthenticatedUserThreadLocal.getUser();

        if (!permissionManager.hasPermission(user, Permission.ADMINISTER, PermissionManager.TARGET_APPLICATION))
        {
            throw new NotPermittedException("You don't have administration permission.");
        }
    }

    public void assertCanExport(Space space) throws RemoteException
    {
        User user = AuthenticatedUserThreadLocal.getUser();

        if (!hasPermission(user, Permission.EXPORT, space))
        {
            throw new NotPermittedException("You don't have permission to export the space: " + space.getKey());
        }
    }

    public void assertCanAdminister(Space space) throws RemoteException
    {
        User user = AuthenticatedUserThreadLocal.getUser();

        if (!hasPermission(user, Permission.ADMINISTER, space))
        {
            throw new NotPermittedException("You don't have permission to administer the space: " + space.getKey());
        }
    }

    private boolean hasPermission(User user, Permission permission, Space space)
    {
        return GeneralUtil.isSuperUser(user) || permissionManager.hasPermission(user, permission, space);
    }
}