package com.atlassian.confluence.rpc.soap.services;

import com.atlassian.confluence.importexport.DefaultExportContext;
import com.atlassian.confluence.importexport.ImportExportManager;
import com.atlassian.confluence.pages.Comment;
import com.atlassian.confluence.pages.Page;
import com.atlassian.confluence.rpc.AlreadyExistsException;
import com.atlassian.confluence.rpc.NotPermittedException;
import com.atlassian.confluence.rpc.RemoteException;
import com.atlassian.confluence.rpc.soap.AbstractSoapTestCase;
import com.atlassian.confluence.rpc.soap.ConfluenceSoapService;
import com.atlassian.confluence.rpc.soap.beans.RemoteSpace;
import com.atlassian.confluence.rpc.soap.beans.RemoteSpaceSummary;
import com.atlassian.confluence.security.*;
import com.atlassian.confluence.setup.BootstrapManager;
import com.atlassian.confluence.spaces.Space;
import com.atlassian.confluence.spaces.SpaceManager;
import com.atlassian.confluence.user.AuthenticatedUserThreadLocal;
import com.atlassian.renderer.WikiStyleRenderer;
import com.atlassian.user.User;
import com.mockobjects.constraint.Constraint;
import com.mockobjects.dynamic.C;
import com.mockobjects.dynamic.FullConstraintMatcher;
import com.mockobjects.dynamic.Mock;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class TestSpacesSoapService extends AbstractSoapTestCase
{
    private SpacesSoapService service;
    private Mock mockSpaceManager;
    private Mock mockRenderer;
    private Mock mockServiceHelper;
    private GateKeeper gateKeeper = new GateKeeper(){
        public void addKey(String path, User user)
        {
        }

        public boolean isAccessPermitted(String path, User user)
        {
            return false;
        }

        public void cleanAllKeys()
        {
        }

        public void allowAnonymousAccess(String path)
        {

        }
    };

    public void setUp() throws Exception
    {
        super.setUp();
        service = new SpacesSoapService();
        service.setGateKeeper(gateKeeper);
        // setup mock objects
        service.setPermissionManager((PermissionManager) mockPermissionManager.proxy());
        service.setSpacePermissionManager((SpacePermissionManager) mockSpacePermissionManager.proxy());
        service.setUserAccessor(userAccessor);

        mockSpaceManager = new Mock(SpaceManager.class);
        service.setSpaceManager((SpaceManager) mockSpaceManager.proxy());

        mockRenderer = new Mock(WikiStyleRenderer.class);
        service.setWikiStyleRenderer((WikiStyleRenderer) mockRenderer.proxy());

        mockServiceHelper = new Mock(SoapServiceHelper.class);
        service.setSoapServiceHelper((SoapServiceHelper) mockServiceHelper.proxy());

        // Set the current user
        AuthenticatedUserThreadLocal.setUser(user);
    }

    public void tearDown() throws Exception
    {
        super.tearDown();

        AuthenticatedUserThreadLocal.setUser(null);
    }

    private void verifyMocks()
    {
        mockSpaceManager.verify();
        mockPermissionManager.verify();
        mockRenderer.verify();
    }

    public void testGetSpaces() throws Exception
    {
        Space fruitSpace = makeSpace("FRUIT");
        Space breadSpace = makeSpace("BREAD");
        List spaces = Arrays.asList(new Object[]{fruitSpace, breadSpace});

        mockSpaceManager.expectAndReturn("getPermittedSpaces", C.args(C.eq(user)), spaces);
//        mockPermissionManager.expectAndReturn("getPermittedEntities", C.args(C.eq(user), C.eq(ConfluenceSoapServiceDelegator.VIEW_SPACE_PERMISSION_TYPES), C.eq(spaces)), spaces);

        RemoteSpaceSummary[] summaries = service.getSpaces();
        assertEquals(new RemoteSpaceSummary(fruitSpace), summaries[0]);
        assertEquals(new RemoteSpaceSummary(breadSpace), summaries[1]);
        verifyMocks();
    }

    public void testGetSpace() throws RemoteException
    {
        Space space = makeSpace("FRUIT");
        mockServiceHelper.expectAndReturn("retrieveSpace", C.args(C.eq("FRUIT")), space);
        assertEquals(new RemoteSpace(space, null), service.getSpace("FRUIT"));
    }

    public void testAddSpace() throws RemoteException
    {
        mockSpaceManager.expectAndReturn("isValidSpaceKey", "FRUIT", Boolean.TRUE);
        Space space = makeSpace("FRUIT");
        mockSpaceManager.expectAndReturn("getSpace", "FRUIT", null);
        mockSpaceManager.expectAndReturn("createSpace", new FullConstraintMatcher(new Constraint[] {
            C.eq(space.getKey()), C.eq(space.getName()), C.IS_NULL, C.eq(user)}), space);
        mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(user), C.eq(PermissionManager.TARGET_APPLICATION), C.eq(Space.class)), true);
        assertEquals("FRUIT", service.addSpace(new RemoteSpace(space, null)).getKey());
        mockSpaceManager.verify();
        mockPermissionManager.verify();
    }

    public void testCannotAddBadSpace() throws RemoteException
    {
        Space space = makeSpace(" AN INVALID SPACE KEY ");
        mockSpaceManager.expectAndReturn("isValidSpaceKey", " AN INVALID SPACE KEY ", Boolean.FALSE);
        try
        {
            service.addSpace(new RemoteSpace(space, null));
            fail("Should have thrown remote exception?");
        }
        catch (RemoteException e)
        {
        }

        mockSpaceManager.verify();
    }
    public void testAddSpaceForConfluenceAdmin() throws RemoteException
        {
            Space space = makeSpace("FRUIT");
            mockSpaceManager.expectAndReturn("isValidSpaceKey", "FRUIT", true);
            mockSpaceManager.expectAndReturn("getSpace", "FRUIT", null);
            mockSpaceManager.expectAndReturn("createSpace", new FullConstraintMatcher(new Constraint[] {
                C.eq(space.getKey()), C.eq(space.getName()), C.IS_NULL, C.eq(user)}), space);
            mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(user), C.eq(PermissionManager.TARGET_APPLICATION), C.eq(Space.class)), true);
            assertEquals("FRUIT", service.addSpace(new RemoteSpace(space, null)).getKey());
            mockSpaceManager.verify();
            mockPermissionManager.verify();
        }

        public void testAddSpaceForSpaceAdmin() throws RemoteException
        {
            Space space = makeSpace("FRUIT");
            mockSpaceManager.expectAndReturn("isValidSpaceKey", "FRUIT", true);
            mockSpaceManager.expectAndReturn("getSpace", "FRUIT", null);
            mockSpaceManager.expectAndReturn("createSpace", new FullConstraintMatcher(new Constraint[] {
                C.eq(space.getKey()), C.eq(space.getName()), C.IS_NULL, C.eq(user)}), space);
            mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(user), C.eq(PermissionManager.TARGET_APPLICATION), C.eq(Space.class)), true);
            assertEquals("FRUIT", service.addSpace(new RemoteSpace(space, null)).getKey());
            mockSpaceManager.verify();
            mockPermissionManager.verify();
        }

    public void testAddExistingSpace() throws RemoteException
    {
        mockSpaceManager.expectAndReturn("isValidSpaceKey", "FRUIT", Boolean.TRUE);
        Space space = makeSpace("FRUIT");
        mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(user), C.eq(PermissionManager.TARGET_APPLICATION), C.eq(Space.class)), true);
        mockSpaceManager.expectAndReturn("getSpace", "FRUIT", space);
        RemoteSpace newSpace = new RemoteSpace();
        newSpace.setKey("FRUIT");
        try
        {
            service.addSpace(newSpace);
            fail("Expected AlreadyExistsException");
        }
        catch (AlreadyExistsException e)
        {
            // expected
        }
    }

    public void testAddSpaceNoPermissions() throws RemoteException
    {
        mockSpaceManager.expectAndReturn("isValidSpaceKey", "FRUIT", Boolean.TRUE);
        mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(user), C.eq(PermissionManager.TARGET_APPLICATION), C.eq(Space.class)), false);
        RemoteSpace newSpace = new RemoteSpace();
        newSpace.setKey("FRUIT");
        try
        {
            service.addSpace(newSpace);
            fail("Expected NotPermittedException");
        }
        catch (NotPermittedException e)
        {
            // expected
        }
    }

    public void testRemoveSpace() throws RemoteException
    {
        Space space = makeSpace("FRUIT");
        mockSpaceManager.expectAndReturn("getSpace", "FRUIT", space);
        mockSpaceManager.expect("removeSpace", space);
        mockPermissionManager.matchAndReturn("hasPermission", C.args(C.eq(SpacePermission.VIEWSPACE_PERMISSION), C.eq(space), C.eq(user)), true);
        mockServiceHelper.expectAndReturn("assertCanView", space, true);
        mockServiceHelper.expectAndReturn("assertCanAdminister", space, true);
        assertEquals(Boolean.TRUE, service.removeSpace("FRUIT"));

        mockSpaceManager.verify();
        mockServiceHelper.verify();
    }

    public void testRemoveSpaceByConfluenceAdmin() throws RemoteException
    {
        Space space = makeSpace("FRUIT");
        mockSpaceManager.expectAndReturn("getSpace", "FRUIT", space);
        mockSpaceManager.expect("removeSpace", space);
        mockPermissionManager.matchAndReturn("hasPermission", C.args(C.eq(SpacePermission.VIEWSPACE_PERMISSION), C.eq(space), C.eq(user)), true);
        mockServiceHelper.expectAndReturn("assertCanView", space, true);
        mockServiceHelper.expectAndReturn("assertCanAdminister", space, true);
        assertEquals(Boolean.TRUE, service.removeSpace("FRUIT"));

        mockSpaceManager.verify();
        mockServiceHelper.verify();
    }

    public void testRemoveNonexistentSpace() throws RemoteException
    {
        mockSpaceManager.expectAndReturn("getSpace", "FRUIT", null);
        mockServiceHelper.matchAndThrow("assertCanView", C.args(C.IS_NULL), new NotPermittedException());
        try
        {
            service.removeSpace("FRUIT");
            fail("Expected exception");
        }
        catch (RemoteException e)
        {
            //expected
        }

        mockSpaceManager.verify();
    }

    public void testRemoveSpaceNoPermissions() throws RemoteException
    {
        Space space = makeSpace("FRUIT");
        mockSpaceManager.expectAndReturn("getSpace", "FRUIT", space);
        mockPermissionManager.matchAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.VIEW), C.eq(space)), true);
        mockServiceHelper.matchAndThrow("assertCanView", space, new NotPermittedException());

        try
        {
            service.removeSpace("FRUIT");
            fail("Expected NotPermittedException");
        }
        catch(NotPermittedException e)
        {
            // expected
        }

        mockSpaceManager.verify();
    }

    public void testGetPermissions() throws Exception
    {
        Space space = makeSpace("FRUIT");
        mockServiceHelper.expectAndReturn("retrieveSpace", C.args(C.eq("FRUIT")), space);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.VIEW), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.EDIT), C.eq(space)), false);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.ADMINISTER), C.eq(space)), false);
        mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(user), C.eq(space), C.eq(Comment.class)), false);
        assertTrue(Arrays.equals(new String[]{ConfluenceSoapService.VIEW_PERMISSION}, service.getPermissions("FRUIT")));

        mockServiceHelper.expectAndReturn("retrieveSpace", C.args(C.eq("FRUIT")), space);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.VIEW), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.EDIT), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.ADMINISTER), C.eq(space)), false);
        mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(user), C.eq(space), C.eq(Comment.class)), false);
        assertTrue(Arrays.equals(new String[]{ConfluenceSoapService.VIEW_PERMISSION, ConfluenceSoapService.MODIFY_PERMISSION}, service.getPermissions("FRUIT")));

        mockServiceHelper.expectAndReturn("retrieveSpace", C.args(C.eq("FRUIT")), space);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.VIEW), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.EDIT), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.ADMINISTER), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(user), C.eq(space), C.eq(Comment.class)), true);
        assertTrue(Arrays.equals(new String[]{ConfluenceSoapService.VIEW_PERMISSION, ConfluenceSoapService.MODIFY_PERMISSION, ConfluenceSoapService.COMMENT_PERMISSION, ConfluenceSoapService.ADMIN_SPACE_PERMISSION}, service.getPermissions("FRUIT")));

        verifyMocks();
    }

    public void testGetPermissionsForUser() throws Exception
    {
        Space space = makeSpace("FRUIT");

        User otherUser = userAccessor.createUser("other");

        mockServiceHelper.expectAndReturn("retrieveSpace", C.args(C.eq("FRUIT")), space);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.ADMINISTER), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.VIEW), C.eq(space)), false);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.EDIT), C.eq(space)), false);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.ADMINISTER), C.eq(space)), false);
        mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(otherUser), C.eq(space), C.eq(Comment.class)), false);
        assertTrue(Arrays.equals(new String[]{}, service.getPermissions("FRUIT", "other")));

        mockServiceHelper.expectAndReturn("retrieveSpace", C.args(C.eq("FRUIT")), space);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.ADMINISTER), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.VIEW), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.EDIT), C.eq(space)), false);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.ADMINISTER), C.eq(space)), false);
        mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(otherUser), C.eq(space), C.eq(Comment.class)), false);
        assertTrue(Arrays.equals(new String[]{ConfluenceSoapService.VIEW_PERMISSION},
                service.getPermissions("FRUIT", "other")));

        mockServiceHelper.expectAndReturn("retrieveSpace", C.args(C.eq("FRUIT")), space);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.ADMINISTER), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.VIEW), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.EDIT), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.ADMINISTER), C.eq(space)), false);
        mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(otherUser), C.eq(space), C.eq(Comment.class)), false);
        assertTrue(Arrays.equals(new String[]{ConfluenceSoapService.VIEW_PERMISSION, ConfluenceSoapService.MODIFY_PERMISSION},
                service.getPermissions("FRUIT", "other")));

        mockServiceHelper.expectAndReturn("retrieveSpace", C.args(C.eq("FRUIT")), space);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.ADMINISTER), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.VIEW), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.EDIT), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(otherUser), C.eq(Permission.ADMINISTER), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(otherUser), C.eq(space), C.eq(Comment.class)), true);
        assertTrue(Arrays.equals(new String[]{ConfluenceSoapService.VIEW_PERMISSION, ConfluenceSoapService.MODIFY_PERMISSION, ConfluenceSoapService.COMMENT_PERMISSION, ConfluenceSoapService.ADMIN_SPACE_PERMISSION},
                service.getPermissions("FRUIT", "other")));

        verifyMocks();
    }

    public void testGetPermissionsForUserNonAdmin() throws Exception
    {
        Space space = makeSpace("FRUIT");

        try
        {
            mockServiceHelper.expectAndReturn("retrieveSpace", C.args(C.eq("FRUIT")), space);
            mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.ADMINISTER), C.eq(space)), false);
            service.getPermissions("FRUIT", "other");
            fail("Expected RemoteException");
        }
        catch (RemoteException e)
        {
            // expected exception
        }

        mockServiceHelper.expectAndReturn("retrieveSpace", C.args(C.eq("FRUIT")), space);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.ADMINISTER), C.eq(space)), false);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.VIEW), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.EDIT), C.eq(space)), true);
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.ADMINISTER), C.eq(space)), false);
        mockPermissionManager.expectAndReturn("hasCreatePermission", C.args(C.eq(user), C.eq(space), C.eq(Comment.class)), true);
        assertTrue(Arrays.equals(new String[]{ConfluenceSoapService.VIEW_PERMISSION, ConfluenceSoapService.MODIFY_PERMISSION, ConfluenceSoapService.COMMENT_PERMISSION},
                service.getPermissions("FRUIT", user.getName())));

        verifyMocks();
    }


    public void testExportSpaceInvalidExportType()
    {
        Space space = makeSpace("FRUIT");

        //this returns an empty list, which will not contain the given export type of "WMA" (see below)
        mockImportExportManager.expectAndReturn("getImportExportTypeSpecifications", new ArrayList());
        service.setImportExportManager((ImportExportManager) mockImportExportManager.proxy());

        try
        {
            service.exportSpace(space.getKey(), "WMA");
            fail("The soap service did not reject an invalid export type for space export.");
        }
        catch (RemoteException e)
        {
        }
    }

    public void testExportSpaceNonExistentSpace()
    {
        List validExportTypes = makeValidExportTypes();

        //this returns an empty list, which will not contain the given export type of "WMA" (see below)
        mockImportExportManager.expectAndReturn("getImportExportTypeSpecifications", validExportTypes);
        service.setImportExportManager((ImportExportManager) mockImportExportManager.proxy());

        mockSpaceManager.expectAndReturn("getSpace", C.args(C.IS_NULL), null);

        try
        {
            service.exportSpace(null, ImportExportManager.TYPE_ALL_DATA);
            fail("The soap service did not reject an invalid space key for space export.");
        }
        catch (RemoteException e)
        {
            e.printStackTrace();
        }
    }

    public void testExportSpace()
    {
        service.setBootstrapManager((BootstrapManager) mockBootstrapManager.proxy());
        mockBootstrapManager.expectAndReturn("getBaseUrl", "localhost:8080/confluence");

        Space space = makeSpace("FRUIT");
        Page testPage = new Page();
        testPage.setTitle("foo");
        testPage.setSpace(space);

        List validExportTypes = makeValidExportTypes();

        mockImportExportManager.expectAndReturn("getImportExportTypeSpecifications", validExportTypes);
        mockImportExportManager.expectAndReturn("getContentTree", C.args(C.eq(user), C.eq(space)), null);
        mockImportExportManager.expectAndReturn("exportAs", C.args(C.eq(ImportExportManager.TYPE_ALL_DATA), C.isA(DefaultExportContext.class)), "theArchive.zip" );
        mockImportExportManager.expectAndReturn("prepareDownloadPath", C.args(C.eq("theArchive.zip")) , "/downloads/theArchive.zip" );

        service.setImportExportManager((ImportExportManager) mockImportExportManager.proxy());
        mockServiceHelper.expectAndReturn("assertCanExport", space, true);

        mockSpaceManager.matchAndReturn("getSpace", C.args(C.eq(space.getKey())), space);

        String downloadPath = null;

        try
        {
            downloadPath = service.exportSpace(space.getKey(), ImportExportManager.TYPE_ALL_DATA);
        }
        catch (RemoteException e)
        {
            fail("The soap service did not export a valid space.");
        }

        assertEquals("localhost:8080/confluence/downloads/theArchive.zip", downloadPath);
        mockImportExportManager.verify();
        mockServiceHelper.verify();
    }


    private List makeValidExportTypes()
    {
        ArrayList validExportTypes = new ArrayList();
        validExportTypes.add(ImportExportManager.TYPE_XML);
        validExportTypes.add(ImportExportManager.TYPE_PDF);
        validExportTypes.add(ImportExportManager.TYPE_HTML);
        validExportTypes.add(ImportExportManager.TYPE_ALL_DATA);

        return validExportTypes;
    }


}