/*
 * 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.brooklyn.core.mgmt.persist.jclouds;

import java.io.IOException;

import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.internal.BrooklynProperties;
import org.apache.brooklyn.core.mgmt.persist.PersistMode;
import org.apache.brooklyn.core.mgmt.persist.PersistenceStoreObjectAccessorWriterTestFixture;
import org.apache.brooklyn.core.mgmt.persist.StoreObjectAccessorLocking;
import org.apache.brooklyn.core.mgmt.persist.PersistenceObjectStore.StoreObjectAccessorWithLock;
import org.apache.brooklyn.core.mgmt.persist.jclouds.JcloudsBlobStoreBasedObjectStore;
import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.net.Urls;
import org.apache.brooklyn.util.text.Identifiers;
import org.apache.brooklyn.util.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

@Test(groups={"Live", "Live-sanity"})
public class JcloudsObjectStoreAccessorWriterTest extends PersistenceStoreObjectAccessorWriterTestFixture {

    private static final Logger log = LoggerFactory.getLogger(JcloudsObjectStoreAccessorWriterTest.class);
    
    private JcloudsBlobStoreBasedObjectStore store;
    private LocalManagementContextForTests mgmt;

    @Override @BeforeMethod
    public void setUp() throws Exception {
        store = new JcloudsBlobStoreBasedObjectStore(
            BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC, BlobStoreTest.CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(4));
        store.injectManagementContext(mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault()));
        store.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);
        super.setUp();
    }

    @Override @AfterMethod(alwaysRun=true)
    public void tearDown() throws Exception {
        super.tearDown();
        if (mgmt!=null) Entities.destroyAll(mgmt);
        if (store!=null) store.deleteCompletely();
    }
    
    @Override
    protected StoreObjectAccessorWithLock newPersistenceStoreObjectAccessor() throws IOException {
        return newPersistenceStoreObjectAccessor(store, "");
    }
    protected StoreObjectAccessorWithLock newPersistenceStoreObjectAccessor(JcloudsBlobStoreBasedObjectStore aStore, String prefix) throws IOException {
        return new StoreObjectAccessorLocking(aStore.newAccessor(prefix+"sample-file-"+Identifiers.makeRandomId(4)));
    }

    @Override
    protected Duration getLastModifiedResolution() {
        // Not sure what timing resolution is on things like Softlayer's Swift.
        // It passed for Aled repeatedly on 2014-11-05 with 2 seconds.
        return Duration.seconds(2);
    }
    
    @Override
    protected int biggishSize() {
        // bit smaller since it's actually uploading here!
        return 10000;
    }

    /** Tests what happen when we ask the store to be in a container with a path, e.g. path1/path2 
     * and then the accessor to a file within that (path3/file) --
     * this does it an emulated way, where the store tracks the subpath so we don't have to */
    @Test(groups={"Live"})
    public void testNestedPath1() throws IOException {
        mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault());
        String path1 = BlobStoreTest.CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(4);
        String path2 = BlobStoreTest.CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(4);
        String path3 = BlobStoreTest.CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(4);
        JcloudsBlobStoreBasedObjectStore store0 = null;
        try {
            store0 = new JcloudsBlobStoreBasedObjectStore(BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC, Urls.mergePaths(path1, path2));
            store0.injectManagementContext(mgmt);
            store0.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);

            newPersistenceStoreObjectAccessor(store0, path3+"/").put("hello world");
        } catch (Exception e) {
            log.warn("Failed with: "+e, e);
            throw Exceptions.propagate(e);
            
        } finally {
            store0.deleteCompletely();
            
            JcloudsBlobStoreBasedObjectStore storeD = new JcloudsBlobStoreBasedObjectStore(BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC, path1);
            storeD.injectManagementContext(mgmt);
            storeD.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);
            storeD.deleteCompletely();
        }
    }

    /** Tests what happen when we ask the store to be in a container with a path, e.g. path1/path2 
     * and then the accessor to a file within that (path3/file) --
     * this does it the "official" way, where we ask for the store's container
     * to be the first path segment */
    @Test(groups={"Live"})
    public void testNestedPath2() throws IOException {
        mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault());
        String path1 = BlobStoreTest.CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(4);
        String path2 = BlobStoreTest.CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(4);
        String path3 = BlobStoreTest.CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(4);
        JcloudsBlobStoreBasedObjectStore store1 = null, store2 = null;
        try {
            store1 = new JcloudsBlobStoreBasedObjectStore(BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC, 
                path1);
            store1.injectManagementContext(mgmt);
            store1.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);
            store1.createSubPath(path2);
            newPersistenceStoreObjectAccessor(store1, path2+"/"+path3+"/").put("hello world");
            
            store2 = new JcloudsBlobStoreBasedObjectStore(BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC, 
                Urls.mergePaths(path1, path2));
            store2.injectManagementContext(mgmt);
            store2.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED);

            newPersistenceStoreObjectAccessor(store2, path3+"/").put("hello world");
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // this doesn't work
//            store2.deleteCompletely();
            // this is how you have to do it:
            store1.newAccessor(path2).delete();
            
            store1.deleteCompletely();
        }
    }

    @Test(groups={"Live", "Live-sanity"})
    @Override
    public void testWriteBacklogThenDeleteWillLeaveFileDeleted() throws Exception {
        super.testWriteBacklogThenDeleteWillLeaveFileDeleted();
    }
    
    @Test(groups={"Live", "Live-sanity"})
    @Override
    public void testWritesFile() throws Exception {
        super.testWritesFile();
    }

    @Test(groups={"Live", "Live-sanity"})
    @Override
    public void testLastModifiedTime() throws Exception {
        super.testLastModifiedTime();
    }
    
    @Test(groups={"Live", "Live-sanity"})
    @Override
    public void testExists() throws Exception {
        super.testExists();
    }
    
    @Test(groups={"Live", "Live-sanity"})
    @Override
    public void testAppendsFile() throws Exception {
        super.testAppendsFile();
    }
}
