/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.jackrabbit.core.version;

import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.Workspace;
import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;

import org.apache.jackrabbit.test.AbstractJCRTest;

/**
 * Test case for JCR-134.
 */
public class RemoveOrphanVersionHistoryTest extends AbstractJCRTest {

    /**
     * Test orphan version history cleaning in a single workspace.
     * @throws RepositoryException if an error occurs.
     */
    public void testRemoveOrphanVersionHistory() throws RepositoryException {
        Node n = testRootNode.addNode(nodeName1);
        n.addMixin(mixVersionable);
        testRootNode.save();
        Session session = n.getSession();
        VersionHistory vh = n.getVersionHistory();
        String vhUuid = vh.getUUID();
        assertExists(session, vhUuid);

        // First version
        Version v10 = n.checkin();
        n.checkout();

        // Second version
        Version v11 = n.checkin();
        n.checkout();

        // Remove node
        n.remove();
        testRootNode.save();
        assertExists(session, vhUuid);

        // Remove the first version
        vh.removeVersion(v10.getName());
        assertExists(session, vhUuid);

        // Remove the second and last version
        vh.removeVersion(v11.getName());

        try {
            session.getNodeByUUID(vhUuid);
            fail("Orphan version history must have been removed");
        } catch (ItemNotFoundException e) {
            // Expected
        }
    }

    /**
     * Test orphan version history cleaning in multiple workspace.
     * @throws RepositoryException if an error occurs.
     */
    public void testWorkspaceRemoveOrphanVersionHistory() throws RepositoryException {
        Node n = testRootNode.addNode(nodeName1);
        n.addMixin(mixVersionable);
        testRootNode.save();
        Session session = n.getSession();
        VersionHistory vh = n.getVersionHistory();
        String vhUuid = vh.getUUID();
        assertExists(session, vhUuid);

        // First version
        Version v10 = n.checkin();
        n.checkout();

        Workspace defaultWorkspace = n.getSession().getWorkspace();
        Session otherWsSession = getHelper().getSuperuserSession(workspaceName);
        // Clone the node in another workspace
        otherWsSession.getWorkspace().clone(defaultWorkspace.getName(), n.getPath(), n.getPath(), false);
        Node otherWsRootNode = otherWsSession.getRootNode();
        Node clonedNode = otherWsRootNode.getNode(n.getPath().substring(1));
        // Ensure that version histories are the same
        assertEquals(vhUuid, clonedNode.getVersionHistory().getUUID());

        Version v11 = clonedNode.checkin();
        clonedNode.checkout();

        // Remove node
        n.remove();
        testRootNode.save();
        assertExists(session, vhUuid);
        assertExists(otherWsSession, vhUuid);

        // Remove the first version
        vh.removeVersion(v10.getName());
        assertExists(session, vhUuid);
        assertExists(otherWsSession, vhUuid);

        // Remove cloned node
        clonedNode.remove();
        otherWsRootNode.save();
        assertExists(session, vhUuid);
        assertExists(otherWsSession, vhUuid);

        // Remove the last version
        vh.removeVersion(v11.getName());

        try {
            session.getNodeByUUID(vhUuid);
            fail("Orphan version history must have been removed from the default workspace");
        } catch (ItemNotFoundException e) {
            // Expected
        }

        try {
            otherWsSession.getNodeByUUID(vhUuid);
            fail("Orphan version history must have been removed from the other workspace");
        } catch (ItemNotFoundException e) {
            // Expected
        }
    }

    /**
     * Test that an emptied version history that is still being referenced
     * from another workspace does not get removed.
     *
     * @throws RepositoryException if an error occurs.
     */
    public void testEmptyNonOrphanVersionHistory() throws RepositoryException {
        Session session = testRootNode.getSession();

        // Create versionable test node
        Node node = testRootNode.addNode(nodeName1);
        node.addMixin(mixVersionable);
        session.save();

        VersionHistory history = node.getVersionHistory();
        String uuid = history.getUUID();

        // Create version 1.0
        Version v10 = node.checkin();

        // Remove the test node
        node.checkout();
        node.remove();
        session.save();

        Session otherSession = getHelper().getReadWriteSession(workspaceName);
        try {
            // create a reference to the version history in another workspace
            Node otherRoot = otherSession.getRootNode();
            Property reference = otherRoot.setProperty(
                    "RemoveOrphanVersionTest", uuid, PropertyType.REFERENCE);
            otherSession.save();

            // Now remove the contents of the version history
            history.removeVersion(v10.getName());

            // Check that the version history still exists!
            try {
                session.getNodeByUUID(uuid);
            } catch (ItemNotFoundException e) {
                fail("Referenced empty version history must note be removed");
            }

            // Cleanup
            reference.remove();
            otherSession.save();
        } finally {
            otherSession.logout();
        }
    }

    /**
     * Assert that a node exists in a session.
     * @param session the session.
     * @param uuid the node's UUID.
     * @throws RepositoryException if an error occurs.
     */
    protected void assertExists(Session session, String uuid) throws RepositoryException
    {
        try {
            session.getNodeByUUID(uuid);
        } catch (ItemNotFoundException e) {
            fail("Unknown uuid: " + uuid);
        }
    }
}
