package jnpf.storecertificatephoto.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jnpf.certificate.mapper.CertificateInstanceItemMapper;
import jnpf.certificate.mapper.CertificateInstanceMapper;
import jnpf.model.certificate.po.CertificateInstanceEntity;
import jnpf.model.certificate.po.CertificateInstanceItemEntity;
import jnpf.model.storecertificatephoto.po.StoreCertificatePhotoEntity;
import jnpf.model.storecertificatephoto.po.StoreCertificatePhotoItemEntity;
import jnpf.model.storecertificatephoto.req.StoreCertificatePhotoAddReq;
import jnpf.model.storecertificatephoto.req.StoreCertificatePhotoItemReq;
import jnpf.model.storecertificatephoto.req.StoreCertificatePhotoUpdateReq;
import jnpf.model.storecertificatephoto.vo.StoreCertificatePhotoIdNameVO;
import jnpf.model.storecertificatephoto.vo.StoreCertificatePhotoItemVO;
import jnpf.model.storecertificatephoto.vo.StoreCertificatePhotoVO;
import jnpf.model.warningnotice.enums.CertificateTypeEnum;
import jnpf.storecertificatephoto.mapper.StoreCertificatePhotoItemMapper;
import jnpf.storecertificatephoto.mapper.StoreCertificatePhotoMapper;
import jnpf.storecertificatephoto.service.StoreCertificatePhotoService;
import jnpf.util.UserProvider;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 门店证件照服务实现
 */
@Service
public class StoreCertificatePhotoServiceImpl extends ServiceImpl<StoreCertificatePhotoMapper, StoreCertificatePhotoEntity>
        implements StoreCertificatePhotoService {

    private static final int ITEM_TYPE_IMAGE = 1;
    private static final int ITEM_TYPE_TEXT = 2;

    @Autowired
    private StoreCertificatePhotoItemMapper storeCertificatePhotoItemMapper;
    @Autowired
    private CertificateInstanceItemMapper certificateInstanceItemMapper;
    @Autowired
    private CertificateInstanceMapper certificateInstanceMapper;
    @Autowired
    private RedissonClient redissonClient;
    private static final String lockPrefix = "certificate:customer:add:";

    /**
     * 新增门店证件照配置
     *
     * @param req 门店证件照请求参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(StoreCertificatePhotoAddReq req) {
        validateAddReq(req);
        String certificateName = req.getCertificateName().trim();

        if (existsCertificateName(certificateName, null)) {
            throw new RuntimeException("证照名称已存在");
        }

        String lockKey = buildCertificateCustomerAddLock();

        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock(10, TimeUnit.SECONDS);
            Long count = baseMapper.selectCount(new LambdaQueryWrapper<StoreCertificatePhotoEntity>()
                    .eq(StoreCertificatePhotoEntity::getEnabledMark,0));
            if(Objects.nonNull(count) && count >= 15){
                throw new RuntimeException("超出15个证照，无法添加!");
            }
        }finally {
            lock.unlock();
        }

        StoreCertificatePhotoEntity entity = req.convert();
        insertPhotoEntity(entity);

        List<StoreCertificatePhotoItemEntity> items = new ArrayList<>();
        items.addAll(buildItems(entity.getId(), req.getImageItemList(), ITEM_TYPE_IMAGE));
        items.addAll(buildItems(entity.getId(), req.getTextItemList(), ITEM_TYPE_TEXT));
        for (StoreCertificatePhotoItemEntity item : items) {
            storeCertificatePhotoItemMapper.insert(item);
        }
    }

    private String buildCertificateCustomerAddLock() {
        return lockPrefix+UserProvider.getUser().getTenantId();
    }

    /**
     * 编辑门店证件照配置
     *
     * @param req 门店证件照编辑参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(StoreCertificatePhotoUpdateReq req) {
        validateAddReq(req);
        String id = req.getId().trim();
        StoreCertificatePhotoEntity dbEntity = getEntityById(id);
        // 先查询当前模板项，再与传入项对比，识别本次删除项。
        List<StoreCertificatePhotoItemEntity> originItems = queryCurrentItems(id);
        List<String> deletedItemIds = collectDeletedItemIds(originItems, req);
        // 删除前先校验是否已被证照实例使用。
        validateDeleteItemReference(deletedItemIds);
        // 未被使用的项执行逻辑删除。
        deleteItemsByIds(deletedItemIds);
        String certificateName = req.getCertificateName().trim();
        if (existsCertificateName(certificateName, id)) {
            throw new RuntimeException("证照名称已存在");
        }

        StoreCertificatePhotoEntity entity = req.convert();
        entity.setId(dbEntity.getId());
        entity.setEnabledMark(dbEntity.getEnabledMark());
        updatePhotoEntity(entity);

        if(!dbEntity.getStatus().equals(entity.getStatus())){
            certificateInstanceMapper.update(null,new LambdaUpdateWrapper<CertificateInstanceEntity>()
                    .eq(CertificateInstanceEntity::getTemplateId, req.getId())
                    .eq(CertificateInstanceEntity::getCertificateType, CertificateTypeEnum.STORE_CUSTOM_CERTIFICATE.getType())
                    .eq(CertificateInstanceEntity::getEnabledMark, 0)
                    .set(CertificateInstanceEntity::getTemplateStatus, req.getStatus())
                    .set(CertificateInstanceEntity::getLastModifyUserId, UserProvider.getLoginUserId())
                    .set(CertificateInstanceEntity::getLastModifyTime, new Date()));
        }

        upsertItems(id, req.getImageItemList(), ITEM_TYPE_IMAGE);
        upsertItems(id, req.getTextItemList(), ITEM_TYPE_TEXT);
    }

    /**
     * 查询门店证件照列表
     *
     * @return 门店证件照列表
     */
    @Override
    public List<StoreCertificatePhotoVO> queryList() {
        LambdaQueryWrapper<StoreCertificatePhotoEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(StoreCertificatePhotoEntity::getEnabledMark, 0);
        queryWrapper.orderByDesc(StoreCertificatePhotoEntity::getCreatorTime);
        List<StoreCertificatePhotoEntity> entityList = baseMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(entityList)) {
            return Collections.emptyList();
        }

        Map<String, List<StoreCertificatePhotoItemEntity>> itemMap = queryItemMap(entityList.stream()
                .map(StoreCertificatePhotoEntity::getId)
                .collect(Collectors.toList()));
        return entityList.stream().map(entity -> buildVO(entity, itemMap)).collect(Collectors.toList());
    }

    @Override
    public List<StoreCertificatePhotoIdNameVO> queryIdNameList() {
        LambdaQueryWrapper<StoreCertificatePhotoEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(StoreCertificatePhotoEntity::getId, StoreCertificatePhotoEntity::getCertificateName);
        queryWrapper.eq(StoreCertificatePhotoEntity::getEnabledMark, 0);
        queryWrapper.orderByDesc(StoreCertificatePhotoEntity::getCreatorTime);
        return baseMapper.selectList(queryWrapper).stream()
                .map(StoreCertificatePhotoIdNameVO::convert)
                .collect(Collectors.toList());
    }

    /**
     * 根据ID查询门店证件照详情
     *
     * @param id 主键ID
     * @return 门店证件照详情
     */
    @Override
    public StoreCertificatePhotoVO queryInfo(String id) {
        String photoId = id.trim();
        StoreCertificatePhotoEntity entity = getEntityById(photoId);
        Map<String, List<StoreCertificatePhotoItemEntity>> itemMap = queryItemMap(Collections.singletonList(photoId));
        return buildVO(entity, itemMap);
    }

    /**
     * 校验新增参数
     *
     * @param req 门店证件照请求参数
     */
    private void validateAddReq(StoreCertificatePhotoAddReq req) {
        if (req.getImageCount() == null || req.getImageCount() < 0) {
            throw new RuntimeException("证照图片数量必须大于等于0");
        }
        if (req.getImageCount() != req.getImageItemList().size()) {
            throw new RuntimeException("证照图片数量必须与图片名称数量一致");
        }

        if (Objects.nonNull(req.getExpireDateEnabled()) &&
                1== req.getExpireDateEnabled()&&
                StrUtil.isBlank(req.getExpireDateName())) {
            throw new RuntimeException("到期日期名称不能为空");
        }

        validateItemNames(req.getImageItemList(), "图片名称");
        validateItemNames(req.getTextItemList(), "文本名称");
        validateItemIds(req.getImageItemList(), req.getTextItemList());
    }

    /**
     * 校验子项名称
     *
     * @param itemList 子项列表
     * @param label    提示文案
     */
    private void validateItemNames(List<StoreCertificatePhotoItemReq> itemList, String label) {
        Set<String> nameSet = new LinkedHashSet<>();
        for (StoreCertificatePhotoItemReq item : itemList) {
            if (item == null || StrUtil.isBlank(item.getItemName())) {
                throw new RuntimeException(label + "不能为空");
            }
            if (!nameSet.add(item.getItemName().trim())) {
                throw new RuntimeException(label + "不能重复");
            }
        }
    }

    /**
     * 校验更新请求中子项ID不能重复
     *
     * @param imageItemList 图片子项列表
     * @param textItemList  文本子项列表
     */
    private void validateItemIds(List<StoreCertificatePhotoItemReq> imageItemList,
                                 List<StoreCertificatePhotoItemReq> textItemList) {
        Set<String> itemIdSet = new LinkedHashSet<>();
        List<StoreCertificatePhotoItemReq> allItems = new ArrayList<>();
        if (CollUtil.isNotEmpty(imageItemList)) {
            allItems.addAll(imageItemList);
        }
        if (CollUtil.isNotEmpty(textItemList)) {
            allItems.addAll(textItemList);
        }
        for (StoreCertificatePhotoItemReq itemReq : allItems) {
            if (itemReq == null || StrUtil.isBlank(itemReq.getId())) {
                continue;
            }
            String itemId = StrUtil.trim(itemReq.getId());
            if (!itemIdSet.add(itemId)) {
                throw new RuntimeException("子项ID不能重复");
            }
        }
    }

    /**
     * 构建门店证件照子项
     *
     * @param photoId     门店证件照ID
     * @param itemReqList 子项请求列表
     * @param itemType    子项类型
     * @return 子项实体列表
     */
    private List<StoreCertificatePhotoItemEntity> buildItems(String photoId,
                                                             List<StoreCertificatePhotoItemReq> itemReqList,
                                                             Integer itemType) {
        List<StoreCertificatePhotoItemEntity> itemList = new ArrayList<>();
        for (int i = 0; i < itemReqList.size(); i++) {
            StoreCertificatePhotoItemReq itemReq = itemReqList.get(i);
            StoreCertificatePhotoItemEntity itemEntity = new StoreCertificatePhotoItemEntity();
            itemEntity.setId(StrUtil.isNotBlank(itemReq.getId()) ? StrUtil.trim(itemReq.getId()) : null);
            itemEntity.setPhotoId(photoId);
            itemEntity.setItemType(itemType);
            itemEntity.setItemName(itemReq.getItemName().trim());
            itemEntity.setTextPrompt(ITEM_TYPE_TEXT == itemType && StrUtil.isNotBlank(itemReq.getTextPrompt())
                    ? itemReq.getTextPrompt().trim()
                    : null);
            itemEntity.setSorts((long) i + 1);
            itemEntity.setEnabledMark(0);
            itemList.add(itemEntity);
        }
        return itemList;
    }

    /**
     * 更新时按子项ID进行增改
     *
     * @param photoId     门店证件照ID
     * @param itemReqList 子项请求列表
     * @param itemType    子项类型
     */
    private void upsertItems(String photoId, List<StoreCertificatePhotoItemReq> itemReqList, Integer itemType) {
        if (CollUtil.isEmpty(itemReqList)) {
            return;
        }

        List<String> itemIds = itemReqList.stream()
                .filter(item -> item != null && StrUtil.isNotBlank(item.getId()))
                .map(item -> StrUtil.trim(item.getId()))
                .distinct()
                .collect(Collectors.toList());

        Map<String, StoreCertificatePhotoItemEntity> dbItemMap = new HashMap<>();
        if (CollUtil.isNotEmpty(itemIds)) {
            LambdaQueryWrapper<StoreCertificatePhotoItemEntity> itemWrapper = Wrappers.lambdaQuery();
            itemWrapper.in(StoreCertificatePhotoItemEntity::getId, itemIds);
            itemWrapper.eq(StoreCertificatePhotoItemEntity::getPhotoId, photoId);
            itemWrapper.eq(StoreCertificatePhotoItemEntity::getEnabledMark, 0);
            List<StoreCertificatePhotoItemEntity> dbItems = storeCertificatePhotoItemMapper.selectList(itemWrapper);
            dbItemMap = dbItems.stream()
                    .collect(Collectors.toMap(StoreCertificatePhotoItemEntity::getId, item -> item, (a, b) -> a));
        }

        List<StoreCertificatePhotoItemEntity> items = buildItems(photoId, itemReqList, itemType);
        for (StoreCertificatePhotoItemEntity item : items) {
            if (StrUtil.isBlank(item.getId())) {
                storeCertificatePhotoItemMapper.insert(item);
                continue;
            }
            StoreCertificatePhotoItemEntity dbItem = dbItemMap.get(item.getId());
            if (dbItem == null) {
                throw new RuntimeException("子项ID不存在或不属于当前配置");
            }
            item.setItemType(itemType);
            item.setEnabledMark(dbItem.getEnabledMark());
            storeCertificatePhotoItemMapper.updateById(item);
        }
    }

    /**
     * 判断证照名称是否已存在
     *
     * @param certificateName 证照名称
     * @param excludeId       排除的主键ID
     * @return 是否存在
     */
    private boolean existsCertificateName(String certificateName, String excludeId) {
        LambdaQueryWrapper<StoreCertificatePhotoEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(StoreCertificatePhotoEntity::getCertificateName, certificateName);
        queryWrapper.eq(StoreCertificatePhotoEntity::getEnabledMark, 0);
        queryWrapper.ne(StrUtil.isNotBlank(excludeId), StoreCertificatePhotoEntity::getId, excludeId);
        return baseMapper.selectCount(queryWrapper) > 0;
    }

    /**
     * 查询当前模板下所有有效项。
     *
     * @param photoId 模板ID
     * @return 当前模板项列表
     */
    private List<StoreCertificatePhotoItemEntity> queryCurrentItems(String photoId) {
        LambdaQueryWrapper<StoreCertificatePhotoItemEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(StoreCertificatePhotoItemEntity::getPhotoId, photoId);
        wrapper.eq(StoreCertificatePhotoItemEntity::getEnabledMark, 0);
        return storeCertificatePhotoItemMapper.selectList(wrapper);
    }

    /**
     * 对比原有模板项与本次请求项，得到被删除的模板项ID。
     *
     * @param originItems 原有模板项
     * @param req         编辑请求
     * @return 待删除模板项ID列表
     */
    private List<String> collectDeletedItemIds(List<StoreCertificatePhotoItemEntity> originItems,
                                               StoreCertificatePhotoUpdateReq req) {
        if (CollUtil.isEmpty(originItems)) {
            return Collections.emptyList();
        }
        Set<String> currentItemIds = new LinkedHashSet<>();
        appendReqItemIds(currentItemIds, req.getImageItemList());
        appendReqItemIds(currentItemIds, req.getTextItemList());

        return originItems.stream()
                .map(StoreCertificatePhotoItemEntity::getId)
                .filter(StrUtil::isNotBlank)
                .map(StrUtil::trim)
                .filter(itemId -> !currentItemIds.contains(itemId))
                .collect(Collectors.toList());
    }

    /**
     * 收集请求中的子项ID（仅收集更新场景中有值的itemId）。
     *
     * @param targetSet   目标ID集合
     * @param itemReqList 请求项列表
     */
    private void appendReqItemIds(Set<String> targetSet, List<StoreCertificatePhotoItemReq> itemReqList) {
        if (CollUtil.isEmpty(itemReqList)) {
            return;
        }
        for (StoreCertificatePhotoItemReq itemReq : itemReqList) {
            if (itemReq == null || StrUtil.isBlank(itemReq.getId())) {
                continue;
            }
            targetSet.add(StrUtil.trim(itemReq.getId()));
        }
    }

    /**
     * 校验待删除模板项是否已被证照实例使用。
     *
     * @param deletedItemIds 待删除模板项ID列表
     */
    private void validateDeleteItemReference(List<String> deletedItemIds) {
        if (CollUtil.isEmpty(deletedItemIds)) {
            return;
        }
        LambdaQueryWrapper<CertificateInstanceItemEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.in(CertificateInstanceItemEntity::getTemplateItemId, deletedItemIds);
        wrapper.eq(CertificateInstanceItemEntity::getEnabledMark, 0);
        Long count = certificateInstanceItemMapper.selectCount(wrapper);
        if (count != null && count > 0) {
            throw new RuntimeException("该字段已经被使用，无法删除");
        }
    }

    /**
     * 执行模板项逻辑删除。
     *
     * @param deletedItemIds 待删除模板项ID列表
     */
    private void deleteItemsByIds(List<String> deletedItemIds) {
        if (CollUtil.isEmpty(deletedItemIds)) {
            return;
        }
        for (String itemId : deletedItemIds) {
            StoreCertificatePhotoItemEntity item = new StoreCertificatePhotoItemEntity();
            item.setId(itemId);
            item.setEnabledMark(1);
            storeCertificatePhotoItemMapper.updateById(item);
        }
    }

    /**
     * 根据ID查询有效的门店证件照配置。
     *
     * @param id 主键ID
     * @return 门店证件照实体
     */
    private StoreCertificatePhotoEntity getEntityById(String id) {
        StoreCertificatePhotoEntity entity = baseMapper.selectById(id);
        if (entity == null || !Integer.valueOf(0).equals(entity.getEnabledMark())) {
            throw new RuntimeException("门店证件照配置不存在");
        }
        return entity;
    }

    /**
     * 查询子项映射
     *
     * @param photoIds 门店证件照ID列表
     * @return 子项映射
     */
    private Map<String, List<StoreCertificatePhotoItemEntity>> queryItemMap(List<String> photoIds) {
        if (CollUtil.isEmpty(photoIds)) {
            return Collections.emptyMap();
        }
        LambdaQueryWrapper<StoreCertificatePhotoItemEntity> itemWrapper = Wrappers.lambdaQuery();
        itemWrapper.in(StoreCertificatePhotoItemEntity::getPhotoId, photoIds);
        itemWrapper.eq(StoreCertificatePhotoItemEntity::getEnabledMark, 0);
        itemWrapper.orderByAsc(StoreCertificatePhotoItemEntity::getPhotoId)
                .orderByAsc(StoreCertificatePhotoItemEntity::getItemType)
                .orderByAsc(StoreCertificatePhotoItemEntity::getSorts);
        return storeCertificatePhotoItemMapper.selectList(itemWrapper).stream()
                .collect(Collectors.groupingBy(StoreCertificatePhotoItemEntity::getPhotoId));
    }

    /**
     * 构建门店证件照返回对象
     *
     * @param entity  门店证件照实体
     * @param itemMap 子项映射
     * @return 门店证件照返回对象
     */
    private StoreCertificatePhotoVO buildVO(StoreCertificatePhotoEntity entity,
                                            Map<String, List<StoreCertificatePhotoItemEntity>> itemMap) {
        StoreCertificatePhotoVO vo = StoreCertificatePhotoVO.convert(entity);
        List<StoreCertificatePhotoItemEntity> currentItems = itemMap.getOrDefault(entity.getId(), Collections.emptyList());
        vo.setImageItemList(currentItems.stream()
                .filter(item -> ITEM_TYPE_IMAGE == item.getItemType())
                .map(StoreCertificatePhotoItemVO::convert)
                .collect(Collectors.toList()));
        vo.setTextItemList(currentItems.stream()
                .filter(item -> ITEM_TYPE_TEXT == item.getItemType())
                .map(StoreCertificatePhotoItemVO::convert)
                .collect(Collectors.toList()));
        return vo;
    }

    /**
     * 新增门店证件照主表数据
     *
     * @param entity 门店证件照实体
     */
    private void insertPhotoEntity(StoreCertificatePhotoEntity entity) {
        try {
            baseMapper.insert(entity);
        } catch (DuplicateKeyException e) {
            throw new RuntimeException("证照名称已存在");
        }
    }

    /**
     * 更新门店证件照主表数据
     *
     * @param entity 门店证件照实体
     */
    private void updatePhotoEntity(StoreCertificatePhotoEntity entity) {
        try {
            baseMapper.updateById(entity);
        } catch (DuplicateKeyException e) {
            throw new RuntimeException("证照名称已存在");
        }
    }
}
