package cn.ey.biz.module.system.service.category;

import cn.ey.biz.common.exception.util.ServiceExceptionUtil;
import cn.ey.biz.common.pojo.PageResult;
import cn.ey.biz.framework.mybatis.core.util.MyBatisUtils;
import cn.ey.biz.module.system.controller.admin.category.vo.*;
import cn.ey.biz.module.system.convert.category.CategoryConvert;
import cn.ey.biz.module.system.dal.dataobject.category.CategoryAppDO;
import cn.ey.biz.module.system.dal.dataobject.category.CategoryDO;
import cn.ey.biz.module.system.dal.dataobject.category.CategoryDataDO;
import cn.ey.biz.module.system.dal.mapper.category.CategoryAppBatChinsertMapper;
import cn.ey.biz.module.system.dal.mapper.category.CategoryAppMapper;
import cn.ey.biz.module.system.dal.mapper.category.CategoryDataMapper;
import cn.ey.biz.module.system.dal.mapper.category.CategoryMapper;
import cn.ey.biz.module.system.dal.redis.RedisKeyConstants;
import cn.ey.biz.module.system.dal.redis.category.CategoryRedisDAO;
import cn.ey.biz.module.system.enums.YesOrNoStatusEnum;
import cn.ey.biz.module.system.enums.category.CategoryDataIdEnum;
import cn.ey.biz.module.system.enums.category.CategorySysFlagEnum;
import cn.ey.biz.module.system.service.config.ConfigService;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import static cn.ey.biz.common.exception.util.ServiceExceptionUtil.exception;
import static cn.ey.biz.module.system.enums.ErrorCodeConstants.*;

/**
 * 字典分类 Service 实现类
 */
@Service
@Validated
public class CategoryServiceImpl implements CategoryService {

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private CategoryAppMapper categoryAppMapper;

    @Resource
    private CategoryDataMapper categoryDataMapper;

    @Resource
    private CategoryAppBatChinsertMapper categoryAppBatChinsertMapper;

    @Resource
    private ConfigService configService;

    @Resource
    private CategoryRedisDAO categoryRedisDAO;

    @Override
    @Transactional
    public Long createCategory(CategoryCreateReqVO createReqVO) {
        // 校验 code 是否存在
        checkCode(createReqVO.getCode());
        // 校验 name 是否存在
        checkName(createReqVO.getName());
        // 插入
        CategoryDO category = CategoryConvert.INSTANCE.convert(createReqVO);

        initCategory(category);
        //添加
        categoryMapper.insert(category);

        for (String app : createReqVO.getAppIds()) {
            CategoryAppDO categoryAppDO = new CategoryAppDO();
            categoryAppDO.setCategoryId(category.getCode());
            categoryAppDO.setApp(app);
            categoryAppMapper.insert(categoryAppDO);
        }

        // 返回
        this.resetCategoryCache();
        return category.getId();
    }

    @Override
    @Transactional
    public void updateCategory(CategoryUpdateReqVO updateReqVO) {
        // 校验存在
        this.validateCategoryExists(updateReqVO.getId());
        // 校验 name 是否存在
        checkNameUpdate(updateReqVO);

        CategoryDO categoryDO = categoryMapper.selectById(updateReqVO.getId());
        if (!categoryDO.getCode().equals(updateReqVO.getCode())) {
            // 校验 code 是否存在
            checkCode(updateReqVO.getCode());
            //更改 关联表CategoryData 中 code
            updateCategoryDataByCode(updateReqVO, categoryDO.getCode());
        }
        if (updateReqVO.getAppIds().size() > 0) {
            categoryAppInsertBatch(updateReqVO);
        }
        // 更新
        CategoryDO updateObj = CategoryConvert.INSTANCE.convert(updateReqVO);
        categoryMapper.updateById(updateObj);
        this.resetCategoryCache();
    }

    @Override
    public void deleteCategory(Long id) {
        // 校验存在
        this.validateCategoryExists(id);
        CategoryDO categoryDO = categoryMapper.selectById(id);
        // 系统类/业务通用字典不能删除
        if (CategorySysFlagEnum.SYSTEM.getCode().equals(categoryDO.getSysFlag())
                || CategorySysFlagEnum.BIZ_COMMON.getCode().equals(categoryDO.getSysFlag())
                || YesOrNoStatusEnum.NO.getCode() == categoryDO.getIsModify()) {
            throw ServiceExceptionUtil.exception(CATEGORY_SYSTEM_NOT_DEL);
        }
        //存在子级字典 无法删除
        Long aLong = categoryDataMapper.selectCount(CategoryDataDO::getCategoryCode, categoryDO.getCode());
        if (aLong > CategoryDataIdEnum.ROOT.getId()) {
            throw ServiceExceptionUtil.exception(CATEGORY_DATA_EXISTS_CHILDREN);
        }
        // 删除
        categoryMapper.deleteById(id);
        this.resetCategoryCache();
    }

    // 校验
    private void validateCategoryExists(Long id) {
        if (categoryMapper.selectById(id) == null) {
            throw exception(CATEGORY_NOT_EXISTS);
        }
    }

    @Override
    public CategoryDO getCategory(Long id) {
        ArrayList<CategoryAppDO> list = new ArrayList<>();
        CategoryDO categoryDO = categoryMapper.selectById(id);
        List<CategoryAppDO> categoryAppDOS = categoryAppMapper.selectListByCateGoryId(id);
        for (CategoryAppDO categoryAppDO : categoryAppDOS) {
            list.add(categoryAppDO);
        }
        categoryDO.setApps(list);
        return categoryDO;
    }

    @Override
    public List<CategoryDO> getCategoryList(Collection<Long> ids) {
        return categoryMapper.selectBatchIds(ids);
    }

    @Override
    public List<CategoryDO> getCategoryList(CategoryExportReqVO exportReqVO) {
        return categoryMapper.selectList(exportReqVO);
    }

    @Override
    public PageResult<CategoryRespVO> getCategoryPage(CategoryPageReqVO pageReqVO) {
        IPage page = MyBatisUtils.buildPage(pageReqVO);
        IPage<CategoryRespVO> result = categoryMapper.selectPage(page, pageReqVO);
        List<CategoryRespVO> categoryRespVOList = result.getRecords().stream().map(record -> {
            if (CollUtil.isNotEmpty(record.getApps())) {
                List<String> list = new ArrayList<>();
                for (CategoryAppRespVO app : record.getApps()) {
                    list.add(app.getApp());
                }
                record.setAppIds(list);
                record.setApps(null);
            }
            return record;
        }).collect(Collectors.toList());
        return new PageResult<CategoryRespVO>(categoryRespVOList, result.getTotal());
    }

    private void checkCode(String code) {
        CategoryDO categoryDO = categoryMapper.selectOne(new LambdaQueryWrapper<CategoryDO>()
                .eq(CategoryDO::getCode, code));
        if (categoryDO != null) {
            throw exception(CATEGORY_CODE_ALREADY_EXISTS);
        }
    }

    private void checkName(String name) {
        CategoryDO categoryDO = categoryMapper.selectByName(name);
        if (categoryDO != null) {
            throw exception(CATEGORY_NAME_ALREADY_EXISTS);
        }
    }

    private void checkNameUpdate(CategoryUpdateReqVO updateReqVO) {
        CategoryDO categoryDO = categoryMapper.selectByName(updateReqVO.getName());
        if (categoryDO != null) {
            if (!updateReqVO.getId().equals(categoryDO.getId())) {
                throw exception(CATEGORY_NAME_ALREADY_EXISTS);
            }
        }
    }

    private void updateCategoryDataByCode(CategoryUpdateReqVO categoryDO, String code) {
        LambdaUpdateWrapper<CategoryDataDO> updateWrapper = Wrappers.<CategoryDataDO>update()
                .lambda()
                .eq(CategoryDataDO::getCategoryCode, code);
        CategoryDataDO categoryDataDO = new CategoryDataDO().setCategoryCode(categoryDO.getCode());
        categoryDataMapper.update(categoryDataDO, updateWrapper);
    }

    private void categoryAppInsertBatch(CategoryUpdateReqVO updateReqVO) {
        LambdaUpdateWrapper<CategoryAppDO> query = new LambdaUpdateWrapper<>();
        query.eq(CategoryAppDO::getCategoryId, updateReqVO.getCode());
        categoryAppMapper.delete(query);

        List<CategoryAppDO> category = updateReqVO.getAppIds().stream().map(app -> new CategoryAppDO()
                .setApp(app)
                .setCategoryId(updateReqVO.getCode())).collect(Collectors.toList());
        categoryAppBatChinsertMapper.saveBatch(category);
    }

    private void initCategory(CategoryDO categoryDO) {
        if (categoryDO != null) {
            if (categoryDO.getIsModify() == null
                    && CategorySysFlagEnum.BIZ_CUSTOM.getCode().equals(categoryDO.getSysFlag())) {
                categoryDO.setIsModify(YesOrNoStatusEnum.YES.getCode());
            }
            categoryDO.setCategoryVersion(1L);
            categoryDO.setIsNewVersion(YesOrNoStatusEnum.YES.getCode());
            categoryDO.setIsVersionControl(YesOrNoStatusEnum.NO.getCode());
        }
    }

    private void resetCategoryCache() {
        Long categoryVersion = categoryRedisDAO.getCategoryVersion(RedisKeyConstants.DICT_REFRESH_VERSION_KEY);
        configService.updateConfigValueByCategoryAndKey("dict", "dict.refresh.version", String.valueOf(categoryVersion));
        categoryRedisDAO.deleteGroup(RedisKeyConstants.DICT_DATA_CACHE_NAME);
    }
}
