package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.driver.storage.BucketObjectDriver;
import com.bocloud.cmp.model.BucketObjectModel;
import com.bocloud.common.model.*;
import com.bocloud.common.utils.Common;
import com.bocloud.common.utils.GridHelper;
import com.bocloud.common.utils.MapTools;
import com.bocloud.ims.common.config.StorageConfig;
import com.bocloud.ims.entity.resource.Bucket;
import com.bocloud.ims.entity.resource.BucketObject;
import com.bocloud.ims.entity.resource.CloudVendor;
import com.bocloud.ims.interfaces.resource.BucketObjectService;
import com.bocloud.ims.repository.resource.BucketObjectRepository;
import com.bocloud.ims.repository.resource.BucketRepository;
import com.bocloud.ims.repository.resource.CloudVendorRepository;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.File;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service("bucketObjectService")
public class BucketObjectServiceImpl implements BucketObjectService {

    Logger logger = LoggerFactory.getLogger(this.getClass().getName());
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private BucketRepository bucketRepository;
    @Autowired
    private BucketObjectRepository bucketObjectRepository;
    @Autowired
    private CloudVendorRepository cloudVendorRepository;
    @Autowired
    private BucketObjectDriver bucketObjectDriver;
    @Autowired
    private ResourceEventPublisher resourceEventPublisher;
    @Autowired
    private StorageConfig storageConfig;

    @Override
    public BocloudResult list(int page, int rows, List<Param> params, Map<String, String> sorter, Boolean simple,
                              RequestContext context) {
        try {
            GridBean gridBean = null;
            if (null == params) {
                params = new ArrayList<>();
            }
            if (null == sorter) {
                sorter = Maps.newHashMap();
            }
            Long tenant = context.getTenant();
            if (null != tenant && 0L != tenant) {
                params.add(new Param(MapTools.simpleMap(Common.TENANTID, tenant), Sign.EQ));
            }
            sorter.put("gmtCreate", Common.ONE);
            List<BucketObject> list = bucketObjectRepository.list(page, rows, params, sorter);
            for (BucketObject object : list) {
                if (null != object.getIsDirectory() && object.getIsDirectory()) {
                    continue;
                }
                object.setObjectSize(converter(object.getSize()));
            }
            int total = bucketObjectRepository.count(params);
            gridBean = GridHelper.getBean(page, rows, total, list);
            return new BocloudResult(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list bucket object failure:", e);
            return new BocloudResult(false, "查询失败", null, null);
        }
    }

    private String converter(long size) {
        String value = null;
        if (size > 1024 * 1024 * 1024) {
            value = new BigDecimal(size).divide(new BigDecimal(1024 * 1024 * 1024), 2, RoundingMode.HALF_UP).toString()
                    + "GB";
        } else if (size > 1024 * 1024) {
            value = new BigDecimal(size).divide(new BigDecimal(1024 * 1024), 2, RoundingMode.HALF_UP).toString() + "MB";
        } else if (size > 1024) {
            value = new BigDecimal(size).divide(new BigDecimal(1024), 2, RoundingMode.HALF_UP).toString() + "KB";
        } else {
            value = size + "B";
        }
        return value;
    }

    @Override
    public BocloudResult upload(BucketObject object, RequestContext context) {
        try {
            String uploadUrl = object.getUploadUrl();
            if (StringUtils.isEmpty(uploadUrl)) {
                return new BocloudResult(false, "上传路径不能为空");
            }
            // 判断文件是否存在
            String filepath = storageConfig.getPath() + object.getUploadUrl();
            File file = new File(filepath);
            if (!file.exists()) {
                return new BocloudResult(false, "文件不存在");
            }
            Bucket bucket = bucketRepository.query(object.getBucketId());
            if (null == bucket) {
                return new BocloudResult(false, "存储桶不存在");
            }
            CloudVendor vendor = cloudVendorRepository.query(bucket.getVendorId());
            if (null == vendor) {
                return new BocloudResult(false, "云平台不存在");
            }
            if (uploadUrl.lastIndexOf("/") < 0) {
                object.setName(uploadUrl);
            } else {
                object.setName(uploadUrl.substring(uploadUrl.lastIndexOf("/") + 1));
            }
            String name = object.getName();
            if (name.lastIndexOf(".") < 0) {
                object.setType("未知");
            } else {
                object.setType(name.substring(name.lastIndexOf(".") + 1));
            }
            Long parentId = object.getParentId();
            BucketObject parentBucketObject = null;
            if (parentId == 0) {
                BucketObject bucketObject = bucketObjectRepository.getByName(name, bucket.getId(), null, 0);
                if (null != bucketObject) {
                    return new BocloudResult(false, "该对象已存在");
                }
            } else {
                parentBucketObject = bucketObjectRepository.query(parentId);
                // 校验对象文件名称是否存在
                BucketObject bucketObject = bucketObjectRepository.getByName(name, bucket.getId(),
                        parentBucketObject.getKey(), parentBucketObject.getObjectIndex() + 1);
                if (null != bucketObject) {
                    return new BocloudResult(false, "该对象已存在");
                }
            }
            // 准备参数，上传文件对象
            object.setIsDirectory(false);
            object.setBucketName(bucket.getName());
            String parentKey = "";
            if (null != parentBucketObject) {
                parentKey = parentBucketObject.getKey();
                object.setParentKey(parentKey);
                object.setObjectIndex(parentBucketObject.getObjectIndex() + 1);
            } else {
                object.setObjectIndex(0);
            }
            object.setKey(name);
            String absolutePath = "";
            if (null != parentBucketObject) {
                absolutePath = parentBucketObject.getAbsolutePath() + name;
            } else {
                absolutePath = name;
            }
            object.setAbsolutePath(absolutePath);
            String objectAcl = object.getObjectAcl();
            if (StringUtils.isEmpty(objectAcl)) {
                objectAcl = bucket.getBucketAcl();
            }
            object.setObjectAcl(objectAcl);
            BucketObjectModel model = new BucketObjectModel();
            BeanUtils.copyProperties(object, model);
            model.setFilepath(filepath);
            model.setValue(name);
            model.setObjectAcl(objectAcl);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    BocloudResult result = new BocloudResult(false, "文件上传失败！");
                    try {
                        result = bucketObjectDriver.upload(model, vendor.getUuid(), bucket.getRegionId());
                        if (result.isFailed()) {
                            sendCreateMessage(vendor, result, context.getTarget(), context);
                            return;
                        }
                        object.setRegionId(bucket.getRegionId());
                        object.setCreatorId(context.getTarget());
                        object.setMenderId(context.getTarget());
                        object.setTenantId(context.getTenant());
                        object.setVendorId(vendor.getId());
                        object.setSize(file.length());
                        object.setBucketUuid(bucket.getBucketUuid());
                        bucketObjectRepository.save(object);
                        result.setMessage("文件上传成功！");
                        logger.info("文件上传成功：" + object.getName());
                        sendCreateMessage(vendor, result, context.getTarget(), context);
                    } catch (Exception e) {
                        logger.error("文件上传失败！" + e.getMessage());
                        sendCreateMessage(vendor, result, context.getTarget(), context);
                    }finally {
                        file.delete();
                    }
                }
            }).start();
            return new BocloudResult(true, "上传任务以下发，正在执行......");
        } catch (Exception e) {
            logger.error("upload bucket object error:", e);
            return new BocloudResult(true, "上传对象失败");
        }
    }

    private void sendCreateMessage(CloudVendor vendor, BocloudResult result, Long userId, RequestContext context) {
        String type = vendor.getType();
        String operate = type.toLowerCase() + ".objectbucket";
        try {
            resourceEventPublisher.send(new OperateResult(result.isSuccess(), result.getMessage(), operate,
                    MapTools.simpleMap("vendorId", vendor.getId()), OperateResult.OperateCategory.ResourceEvent,
                    context));
        } catch (Exception e) {
            logger.error("发送消息失败", e);
        }
    }

    @Override
    public BocloudResult detail(Long id, RequestContext context) {
        try {
            BucketObject object = bucketObjectRepository.query(id);
            if (object == null) {
                return new BocloudResult(false, "对象不存在");
            }
            return new BocloudResult(true, object, "查询成功");
        } catch (Exception e) {
            logger.error("query bucket object error", e);
            return new BocloudResult(false, "查询失败");
        }

    }

    @Override
    public BocloudResult remove(Long id, RequestContext context) {
        String path = BucketObject.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            BucketObject object = bucketObjectRepository.query(id);
            Assert.notNull(object, "文件对象不存在");
            if (null != object.getIsDirectory() && object.getIsDirectory()) {
                List<Param> params = new ArrayList<>();
                params.add(new Param(MapTools.simpleMap("parentKey", object.getKey()), Sign.EQ));
                params.add(new Param(MapTools.simpleMap("objectIndex", object.getObjectIndex() + 1), Sign.EQ));
                int count = bucketObjectRepository.count(params);
                if (count > 0) {
                    return new BocloudResult(false, "该文件夹下存在文件对象，无法删除");
                }
            }
            Bucket bucket = bucketRepository.query(object.getBucketId());
            Assert.notNull(bucket, "文件对象所在的存储桶不存在");
            CloudVendor vendor = cloudVendorRepository.query(object.getVendorId());
            Assert.notNull(vendor, "云平台不存在");
            BucketObjectModel model = new BucketObjectModel();
            model.setBucketName(bucket.getName());
            model.setKey(object.getAbsolutePath());
            BocloudResult result = bucketObjectDriver.remove(model, vendor.getUuid(), bucket.getRegionId());
            if (result.isFailed()) {
                return result;
            }
            bucketObjectRepository.delete(id, context.getTarget());
            return new BocloudResult(true, "删除成功");
        } catch (Exception e) {
            logger.error("remove bucket object error:", e);
            return new BocloudResult(false, "删除失败");
        }
    }

    @Override
    public BocloudResult download(Long id) {
        try {
            BucketObject item = bucketObjectRepository.query(id);
            if (item == null) {
                return new BocloudResult(false, "bucket对象不存在");
            }
            BucketObjectModel model = new BucketObjectModel();
            BeanUtils.copyProperties(item, model);
            CloudVendor vendor = cloudVendorRepository.query(item.getVendorId());
            if (vendor == null) {
                return null;
            }
            InputStream inputStream = bucketObjectDriver.download(model, vendor.getUuid(), item.getRegionId());
            byte[] bytes = new byte[inputStream.available()];
            inputStream.read(bytes);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("name", item.getName());
            jsonObject.put("fileBytes", bytes);
            inputStream.close();
            return new BocloudResult(true, jsonObject, "bucket对象下载成功");
        } catch (Exception e) {
            logger.error("download bucket object error:", e);
            return new BocloudResult(false, "bucket对象下载失败");
        }
    }

    @Override
    public BocloudResult acl(BucketObject object, RequestContext context) {
        String path = BucketObject.class.getSimpleName() + "_" + object.getId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            BucketObject bucketObject = bucketObjectRepository.query(object.getId());
            Assert.notNull(bucketObject, "文件对象不存在");

            Bucket bucket = bucketRepository.query(bucketObject.getBucketId());
            Assert.notNull(bucket, "文件对象所在的存储桶不存在");


            CloudVendor vendor = cloudVendorRepository.query(bucket.getVendorId());
            Assert.notNull(vendor, "云平台不存在");

            BucketObjectModel model = new BucketObjectModel();
            model.setBucketName(bucket.getName());
            model.setAcl(object.getObjectAcl());
            model.setAbsolutePath(bucketObject.getAbsolutePath());
            BocloudResult result = bucketObjectDriver.acl(model, vendor.getUuid(), bucket.getRegionId());
            if (result.isFailed()) {
                return result;
            }
            bucketObject.setObjectAcl(object.getObjectAcl());
            bucketObject.setMenderId(context.getTarget());
            bucketObject.setGmtModify(new Date());
            bucketObjectRepository.update(bucketObject);
            return new BocloudResult(true, "存储桶访问策略设置成功");
        } catch (Exception e) {
            logger.error("acl bucket fail:", e);
            return new BocloudResult(false, "存储桶访问策略设置失败");
        }
    }

    @Override
    public BocloudResult mkdir(BucketObject object, RequestContext context) {
        try {
            logger.info("验证文件夹名称是否重复: {}",object.getName());
            List<BucketObject> objects = bucketObjectRepository.queryByParentId(object.getParentId());
            long count = objects.stream().filter(a -> a.getName().equals(object.getName())).count();
            if(0 < count){
                return new BocloudResult(false, "该文件夹名称已存在");
            }
            // 判断文件是否存在
            String filepath = storageConfig.getPath() + "/folder.txt";
            File file = new File(filepath);
            if (!file.exists()) {
                if (!file.createNewFile()) {
                    logger.error("文件创建失败");
                }
            }
            Bucket bucket = bucketRepository.query(object.getBucketId());
            if (null == bucket) {
                return new BocloudResult(false, "存储桶不存在");
            }
            CloudVendor vendor = cloudVendorRepository.query(bucket.getVendorId());
            if (null == vendor) {
                return new BocloudResult(false, "云平台不存在");
            }
            String parentKey = "";
            if (0 != object.getParentId()) {
                BucketObject parent = bucketObjectRepository.query(object.getParentId());
                if (null == parent) {
                    return new BocloudResult(false, "上级文件不存在");
                }
                BucketObject bucketObject = bucketObjectRepository.getByName(object.getName(), bucket.getId(),
                        parent.getKey(), parent.getObjectIndex());
                if (null != bucketObject) {
                    return new BocloudResult(false, "该对象已存在");
                }
                parentKey = parent.getAbsolutePath();
                object.setParentKey(parent.getKey());
                object.setObjectIndex(parent.getObjectIndex() + 1);
            } else {
                object.setObjectIndex(0);
            }
            object.setLastModified(new Date());
            final String parentKeys = parentKey;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    String type = vendor.getType();
                    String operate = type.toLowerCase() + ".objectbucket";
                    try {
                        object.setType("Folder");
                        object.setBucketUuid(bucket.getBucketUuid());
                        object.setBucketName(bucket.getName());
                        // 准备参数，上传文件对象
                        object.setIsDirectory(true);
                        object.setObjectAcl(bucket.getBucketAcl());

                        String key = parentKeys + object.getName() + "/";
                        object.setKey(object.getName() + "/");
                        object.setAbsolutePath(key);
                        object.setRegionId(bucket.getRegionId());
                        BucketObjectModel model = new BucketObjectModel();
                        BeanUtils.copyProperties(object, model);
                        model.setFilepath(filepath);
                        model.setValue(object.getName());
                        BocloudResult result = bucketObjectDriver.upload(model, vendor.getUuid(), bucket.getRegionId());
                        if (result.isFailed()) {
                            resourceEventPublisher.send(new OperateResult(false, "创建文件夹失败！", operate,
                                    MapTools.simpleMap("vendorId", vendor.getId()),
                                    OperateResult.OperateCategory.ResourceEvent, context));
                            return;
                        }
                        object.setCreatorId(context.getTarget());
                        object.setMenderId(context.getTarget());
                        object.setTenantId(context.getTenant());
                        object.setVendorId(vendor.getId());
                        object.setSize(file.length());
                        object.setLastModified(new Date());
                        bucketObjectRepository.save(object);
                        file.delete();
                        result = new BocloudResult(true, "创建文件夹成功");
                        resourceEventPublisher.send(new OperateResult(true, "创建文件夹成功！", operate,
                                MapTools.simpleMap("vendorId", vendor.getId()),
                                OperateResult.OperateCategory.ResourceEvent, context));
                    } catch (Exception e) {
                        logger.error("目录创建失败：" + e.getMessage());
                        resourceEventPublisher.send(new OperateResult(false, "创建文件夹失败！", operate,
                                MapTools.simpleMap("vendorId", vendor.getId()),
                                OperateResult.OperateCategory.ResourceEvent, context));
                    }
                }
            }).start();
            return new BocloudResult(true, "任务以下发，正在执行......");
        } catch (Exception e) {
            logger.error("upload bucket object error:", e);
            return new BocloudResult(true, "创建文件夹失败");
        }
    }

}
