/*
 * Copyright 2014 the original author or authors.
 *
 * Licensed 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.cloudfoundry.community.servicebroker.s3.plan.basic;

import com.amazonaws.services.identitymanagement.model.AccessKey;
import com.amazonaws.services.identitymanagement.model.User;
import com.amazonaws.services.s3.model.Bucket;
import org.cloudfoundry.community.servicebroker.exception.ServiceBrokerException;
import org.cloudfoundry.community.servicebroker.model.ServiceDefinition;
import org.cloudfoundry.community.servicebroker.model.ServiceInstance;
import org.cloudfoundry.community.servicebroker.model.ServiceInstanceBinding;
import org.cloudfoundry.community.servicebroker.s3.plan.Plan;
import org.cloudfoundry.community.servicebroker.s3.service.S3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class BasicPlan implements Plan {
    public static final String PLAN_ID = "s3-basic-plan";
    public static final String AMAZON_S3_HOST = "s3.amazonaws.com";
    private final BasicPlanIam iam;
    private final S3 s3;

    @Autowired
    public BasicPlan(BasicPlanIam iam, S3 s3) {
        this.iam = iam;
        this.s3 = s3;
    }

    public static org.cloudfoundry.community.servicebroker.model.Plan getPlan() {
        return new org.cloudfoundry.community.servicebroker.model.Plan(PLAN_ID, "basic", "An S3 plan providing a single bucket with unlimited storage.",
                getPlanMetadata());
    }

    private static Map<String, Object> getPlanMetadata() {
        Map<String, Object> planMetadata = new HashMap<String, Object>();
        planMetadata.put("bullets", getPlanBullets());
        return planMetadata;
    }

    private static List<String> getPlanBullets() {
        return Arrays.asList("Single S3 bucket", "Unlimited storage", "Unlimited number of objects");
    }

    public ServiceInstance createServiceInstance(ServiceDefinition service, String serviceInstanceId, String planId,
                                                 String organizationGuid, String spaceGuid) {
        Bucket bucket = s3.createBucketForInstance(serviceInstanceId, service, planId, organizationGuid, spaceGuid);
        iam.createGroupForInstance(serviceInstanceId, bucket.getName());
        iam.applyGroupPolicyForInstance(serviceInstanceId, bucket.getName());
        return new ServiceInstance(serviceInstanceId, service.getId(), planId, organizationGuid, spaceGuid, null);
    }

    public ServiceInstance deleteServiceInstance(String id) {
        ServiceInstance instance = s3.findServiceInstance(id);
        // TODO we need to make these deletes idempotent so we can handle retries on error
        iam.deleteGroupPolicyForInstance(id);
        iam.deleteGroupForInstance(id);
        s3.emptyBucket(id);
        s3.deleteBucket(id);
        return instance;
    }

    public ServiceInstanceBinding createServiceInstanceBinding(String bindingId, ServiceInstance serviceInstance,
                                                               String serviceId, String planId, String appGuid) {
        User user = iam.createUserForBinding(bindingId);
        AccessKey accessKey = iam.createAccessKey(user);
        // TODO create password and add to credentials
        iam.addUserToGroup(user, iam.getGroupNameForInstance(serviceInstance.getId()));
        String bucketName = s3.getBucketNameForInstance(serviceInstance.getId());
        Map<String, Object> credentials = new HashMap<String, Object>();
        credentials.put("bucket", bucketName);
        credentials.put("username", user.getUserName());
        credentials.put("access_key_id", accessKey.getAccessKeyId());
        credentials.put("secret_access_key", accessKey.getSecretAccessKey());
        credentials.put("host", AMAZON_S3_HOST);
        credentials.put("uri", this.generateUri(accessKey.getAccessKeyId(), accessKey.getSecretAccessKey(), bucketName));
        return new ServiceInstanceBinding(bindingId, serviceInstance.getId(), credentials, null, appGuid);
    }

    private String generateUri(String accessKeyId, String secretAccessKey, String bucketName){
        try {
            accessKeyId = URLEncoder.encode(accessKeyId, "UTF-8");
            secretAccessKey = URLEncoder.encode(secretAccessKey, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            //let accessKeyId and secretAccessKey like they are
        }
        return String.format("s3://%s:%s@%s/%s",
                accessKeyId,
                secretAccessKey,
                AMAZON_S3_HOST,
                bucketName
        );
    }

    public ServiceInstanceBinding deleteServiceInstanceBinding(String bindingId, ServiceInstance serviceInstance,
                                                               String serviceId, String planId) throws ServiceBrokerException {
        // TODO make operations idempotent so we can handle retries on error
        iam.removeUserFromGroupForInstance(bindingId, serviceInstance.getId());
        iam.deleteUserAccessKeysForBinding(bindingId);
        iam.deleteUserForBinding(bindingId);
        return new ServiceInstanceBinding(bindingId, serviceInstance.getId(), null, null, null);
    }

    public List<ServiceInstance> getAllServiceInstances() {
        return s3.getAllServiceInstances();
    }

    public ServiceInstance getServiceInstance(String id) {
        return s3.findServiceInstance(id);
    }
}