package com.bocloud.ims.service.resource;

import com.alibaba.fastjson.JSONObject;
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.entity.common.Business;
import com.bocloud.ims.entity.common.Project;
import com.bocloud.ims.entity.common.Tenant;
import com.bocloud.ims.entity.common.User;
import com.bocloud.ims.entity.resource.NameRule;
import com.bocloud.ims.interfaces.resource.NameRuleService;
import com.bocloud.ims.repository.resource.NameRuleRepository;
import com.bocloud.ims.repository.resource.ServerLabelRepository;
import com.bocloud.ims.service.internal.SmsBusinessService;
import com.bocloud.ims.service.internal.SmsProjectService;
import com.bocloud.ims.service.internal.SmsTenantService;
import com.bocloud.ims.service.internal.SmsUserService;
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.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 命名规则service实现
 *
 * @author wangyu
 * @version 3.0.0-SNAPSHOT
 * @since 2018/7/2
 */
@Service("nameRuleService")
public class NameRuleServiceImpl implements NameRuleService {

    private static final Logger logger = LoggerFactory.getLogger(NameRuleServiceImpl.class);

    @Autowired
    private NameRuleRepository nameRuleRepository;
    @Autowired
    private ServerLabelRepository serverLabelRepository;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private SmsTenantService smsTenantService;
    @Autowired
    private SmsUserService smsUserService;
    @Autowired
    private SmsProjectService smsProjectService;
    @Autowired
    private SmsBusinessService smsBusinessService;

    @Override
    public BocloudResult<GridBean<NameRule>> list(Pager pager, RequestContext context) {
        List<NameRule> list = null;
        int total = 0;
        GridBean gridBean = null;
        try {
            List<Param> params = Optional.ofNullable(pager.getParams()).orElse(new ArrayList<>());
            Map<String, String> sorter = Optional.ofNullable(pager.getSorter()).orElse(Maps.newHashMap());
            Boolean simple = Optional.ofNullable(pager.getSimple()).orElse(false);
            Integer page = Optional.ofNullable(pager.getPage()).orElse(1);
            Integer rows = Optional.ofNullable(pager.getRows()).orElse(10);

            if (null != context.getTenant() && 0L != context.getTenant()) {
                BocloudResult<User> result = smsUserService.detail(context.getTarget());
                if (result.isFailed()) {
                    return new BocloudResult<>(false, "登录用户存在问题!");
                }
                User loginUser = result.getData();
                if (null == loginUser) {
                    return new BocloudResult<>(false, "登录用户存在问题!");
                }
                params.add(new Param(MapTools.simpleMap(Common.TENANTID, loginUser.getTenantId()), Sign.EQ));
            } else {
                params.add(new Param(MapTools.simpleMap(Common.TENANTID, 0L), Sign.EQ));
            }
            total = nameRuleRepository.count(params);
            if (simple) {
                List<NameRule> beans = this.nameRuleRepository.list(params);
                gridBean = new GridBean(1, 1, total, beans);
            } else {
                list = nameRuleRepository.list(page, rows, params, sorter);
                gridBean = GridHelper.getBean(page, rows, total, list);
            }
            return new BocloudResult<>(true, gridBean, "查询成功");
        } catch (Exception e) {
            logger.error("list name rule failure:", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    public BocloudResult<NameRule> detail(Long id) {
        try {
            NameRule nameRule = nameRuleRepository.query(id);
            return new BocloudResult<>(true, nameRule, "查询成功");
        } catch (Exception e) {
            logger.error("Get error message：", e);
            return new BocloudResult<>(false, "查询失败");
        }
    }

    @Override
    public BocloudResult<NameRule> create(NameRule nameRule, RequestContext context) {
        try {
            if (nameRuleRepository.exists(nameRule.getName(), context.getTenant())) {
                return new BocloudResult<>(false, "名称已存在");
            }
            if (nameRuleRepository.exists(nameRule.getHasTenant(), nameRule.getHasUser(), nameRule.getHasBusiness(),
                    nameRule.getHasProject(), nameRule.getCustomize(), nameRule.getSuffixType(), context.getTenant())) {
                return new BocloudResult<>(false, "存在重复的命名规则");
            }
            if (null == nameRule.getCurrentNum()) {
                nameRule.setCurrentNum(0L);
            }
            nameRule.setTenantId(context.getTenant());
            nameRuleRepository.save(nameRule);
            return new BocloudResult<>(true, nameRule, "添加成功");
        } catch (Exception e) {
            logger.error("Create name rule error:", e);
            return new BocloudResult<>(false, "添加失败");
        }
    }

    @Override
    public BocloudResult modify(NameRule nameRule, RequestContext context) {
        String path = NameRule.class.getSimpleName() + "_" + nameRule.getId();
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            NameRule oldRule = this.nameRuleRepository.query(nameRule.getId());
            if (null == oldRule) {
                logger.warn("Name rule does not exist!");
                return new BocloudResult<>(false, "数据不存在");
            }
            if (!nameRule.getName().equals(oldRule.getName())) {
                if (nameRuleRepository.exists(nameRule.getName(), context.getTenant())) {
                    return new BocloudResult<>(false, "名称已存在");
                }
            }
            oldRule.setName(nameRule.getName());
            if (nameRule.getHasTenant().equals(oldRule.getHasTenant())
                    || nameRule.getHasUser().equals(oldRule.getHasUser())
                    || nameRule.getHasBusiness().equals(oldRule.getHasBusiness())
                    || nameRule.getHasProject().equals(oldRule.getHasProject())
                    || !nameRule.getCustomize().equals(oldRule.getCustomize())
                    || nameRule.getSuffixType() != oldRule.getSuffixType()) {
                if (nameRuleRepository.exists(nameRule.getHasTenant(), nameRule.getHasUser(), nameRule.getHasBusiness(),
                        nameRule.getHasProject(), nameRule.getCustomize(), nameRule.getSuffixType(),
                        context.getTenant())) {
                    return new BocloudResult<>(false, "存在重复的命名规则");
                }
                oldRule.setSuffixType(nameRule.getSuffixType());
                oldRule.setSuffixLength(nameRule.getSuffixLength());
                oldRule.setHasTenant(nameRule.getHasTenant());
                oldRule.setHasUser(nameRule.getHasUser());
                oldRule.setHasBusiness(nameRule.getHasBusiness());
                oldRule.setHasProject(nameRule.getHasProject());
                oldRule.setCustomize(nameRule.getCustomize());
            }
            this.nameRuleRepository.update(oldRule);
            return new BocloudResult<>(true, "修改成功");
        } catch (Exception e) {
            logger.error("Modify name rule error:", e);
            return new BocloudResult<>(false, "修改失败");
        }
    }

    @Override
    public BocloudResult remove(Long id) {
        String path = NameRule.class.getSimpleName() + "_" + id;
        try (BocloudACLock lock = lockFactory.getACLock(path)) {
            Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
            NameRule nameRule = this.nameRuleRepository.query(id);
            if (null == nameRule) {
                logger.warn("Name rule does not exist!");
                return new BocloudResult<>(false, "数据不存在");
            }
            this.nameRuleRepository.remove(id);
            this.serverLabelRepository.deleteByLabel(id);
            return new BocloudResult<>(true, "删除成功");
        } catch (Exception e) {
            logger.error("Remove name rule error:", e);
            return new BocloudResult<>(false, "删除失败");
        }
    }

    @Override
    public BocloudResult<String> generate(Long id, JSONObject object) {
        try {
            NameRule nameRule = this.nameRuleRepository.query(id);
            if (null == nameRule) {
                logger.warn("Name rule does not exist!");
                return new BocloudResult<>(false, "命名规则不存在");
            }
            Long tenantId = object.getLong("tenantId");
            Long userId = object.getLong("userId");
            Long projectId = object.getLong("projectId");
            String customize = nameRule.getCustomize();
            String name = "";
            if (null != tenantId && 0L != tenantId && null != nameRule.getHasTenant() && nameRule.getHasTenant()) {
                BocloudResult<Tenant> result = smsTenantService.detail(tenantId);
                if (result.isFailed()) {
                    return new BocloudResult<>(false, "命名生成失败，租户不存在");
                }
                Tenant tenant = result.getData();
                if (null == tenant) {
                    return new BocloudResult<>(false, "命名生成失败，租户不存在");
                }
                name += tenant.getCode();
            }
            if (null != userId && 0L != userId && null != nameRule.getHasUser() && nameRule.getHasUser()) {
                BocloudResult<User> result = smsUserService.detail(userId);
                if (result.isFailed()) {
                    return new BocloudResult<>(false, "命名生成失败，用户不存在");
                }
                User user = result.getData();
                if (null == user) {
                    return new BocloudResult<>(false, "命名生成失败，用户不存在");
                }
                if (StringUtils.isEmpty(name)) {
                    name += user.getAccount();
                } else {
                    name += "_" + user.getAccount();
                }
            }
            if (null != projectId && 0L != projectId) {
                BocloudResult<Project> result = smsProjectService.basic(projectId);
                if (result.isFailed()) {
                    return new BocloudResult<>(false, "命名生成失败，项目不存在");
                }
                Project project = result.getData();
                if (null == project) {
                    return new BocloudResult<>(false, "命名生成失败，项目不存在");
                }
                if (null != nameRule.getHasBusiness() && nameRule.getHasBusiness()) {
                    BocloudResult<Business> businessResult = smsBusinessService.detail(project.getBusinessId());
                    if (businessResult.isFailed()) {
                        return new BocloudResult<>(false, "命名生成失败，业务不存在");
                    }
                    Business business = businessResult.getData();
                    if (null == business) {
                        return new BocloudResult<>(false, "命名生成失败，业务不存在");
                    }
                    if (StringUtils.isEmpty(name)) {
                        name += business.getCode();
                    } else {
                        name += "_" + business.getCode();
                    }
                }
                if (null != nameRule.getHasProject() && nameRule.getHasProject()) {
                    if (StringUtils.isEmpty(name)) {
                        name += project.getCode();
                    } else {
                        name += "_" + project.getCode();
                    }
                }
            }
            if (!StringUtils.isEmpty(customize)) {
                if (StringUtils.isEmpty(name)) {
                    name += customize;
                } else {
                    name += "_" + customize;
                }
            }
            // 后缀
            String suffixType = nameRule.getSuffixType();
            String suffix = null;
            if (NameRule.SuffixType.Millis.name().equalsIgnoreCase(suffixType)) {
                suffix = String.valueOf(System.currentTimeMillis());
            } else if (NameRule.SuffixType.RandomString.name().equalsIgnoreCase(suffixType)) {
                Long suffixLength = nameRule.getSuffixLength();
                if (null == suffixLength || 0L == suffixLength) {
                    suffixLength = 10L;
                }
                suffix = getRandomString(suffixLength.intValue());
            } else if (NameRule.SuffixType.Number.name().equalsIgnoreCase(suffixType)) {
                Long nextNum = null == nameRule.getCurrentNum() ? 1L : nameRule.getCurrentNum();
                suffix = String.valueOf(nextNum);
                // 更新当前编号
                nameRule.setCurrentNum(nameRule.getCurrentNum() + 1L);
                String path = NameRule.class.getSimpleName() + "_" + id;
                try (BocloudACLock lock = lockFactory.getACLock(path)) {
                    Assert.isTrue(lock.acquire(20, TimeUnit.SECONDS), "请求超时");
                    nameRuleRepository.update(nameRule);
                } catch (Exception e) {
                    logger.error("Remove name rule error:", e);
                    return new BocloudResult<>(false, "生成失败");
                }
            }
            return new BocloudResult<>(true, name + "_" + suffix, "生成成功");
        } catch (Exception e) {
            logger.error("Remove name rule error:", e);
            return new BocloudResult<>(false, "生成失败");
        }
    }

    // 生成指定length的随机字符串（A-Z，a-z，0-9）
    public static String getRandomString(int length) {
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(str.length());
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }

}
