package jnpf.franchisee.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.toolkit.Wrappers;
import jnpf.franchisee.service.FranchiseeCustomFieldConfigService;
import jnpf.model.franchisee.enums.FranchiseeCustomFieldTypeEnum;
import jnpf.model.franchisee.req.FranchiseeCustomFieldConfigOptionReq;
import jnpf.model.franchisee.req.FranchiseeCustomFieldConfigReq;
import jnpf.model.franchisee.vo.FranchiseeCustomFieldConfigOptionVO;
import jnpf.model.franchisee.vo.FranchiseeCustomFieldConfigVO;
import jnpf.model.warningnotice.po.FtbParamEntity;
import jnpf.storecertificatephoto.mapper.BaseParamMapper;
import jnpf.util.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
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 javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 加盟商自定义字段配置服务实现
 */
@Service
public class FranchiseeCustomFieldConfigServiceImpl implements FranchiseeCustomFieldConfigService {

    /**
     * 自定义字段配置类型
     */
    private static final String customerFieldType = "customerFieldType";
    /**
     * 自定义名称配置：固定key，同时type也固定为该值。
     */
    private static final String customerNameType = "franchiseeCustomerNameType";
    private static final String lockPrefix = "franchisee:customer:field:";

    @Autowired
    private BaseParamMapper baseParamMapper;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 新增自定义字段配置
     *
     * @param req 请求参数
     * @return 配置key
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String add(FranchiseeCustomFieldConfigReq req) {
        return saveOrUpdate(req);
    }

    /**
     * 更新自定义字段配置
     *
     * @param req 请求参数
     * @return 配置key
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String update(FranchiseeCustomFieldConfigReq req) {
        String key = req.getKey();
        if(StringUtil.isBlank(key)){
            throw new ServiceException("key不能为空");
        }
        return saveOrUpdate(req);
    }

    /**
     * 查询所有自定义字段配置
     *
     * @return 配置列表
     */
    @Override
    public List<FranchiseeCustomFieldConfigVO> queryList() {
        LambdaQueryWrapper<FtbParamEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(FtbParamEntity::getType, customerFieldType);
        queryWrapper.orderByAsc(FtbParamEntity::getSort);
        List<FtbParamEntity> entityList = baseParamMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(entityList)) {
            return new ArrayList<>();
        }
        List<FranchiseeCustomFieldConfigVO> result = new ArrayList<>();
        for (FtbParamEntity entity : entityList) {
            FranchiseeCustomFieldConfigVO vo = buildFranchiseeCustomerFieldVo(entity);
            if (vo == null) {
                continue;
            }
            result.add(vo);
        }
        return result;
    }

    @Override
    public FranchiseeCustomFieldConfigVO query(String key) {
        FtbParamEntity ftbParamEntity = baseParamMapper.selectByKey(key);
        if(Objects.isNull(ftbParamEntity)){
            throw new ServiceException("找不到该配置!",404);
        }
        FranchiseeCustomFieldConfigVO franchiseeCustomFieldConfigVO = buildFranchiseeCustomerFieldVo(ftbParamEntity);
        if(Objects.isNull(franchiseeCustomFieldConfigVO)){
            throw new ServiceException("该配置不正确!",404);
        }
        return franchiseeCustomFieldConfigVO;
    }

    private FranchiseeCustomFieldConfigVO buildFranchiseeCustomerFieldVo(FtbParamEntity entity) {
        FranchiseeCustomFieldConfigVO vo = JsonUtil.getJsonToBean(entity.getValue(), FranchiseeCustomFieldConfigVO.class);
        if (vo == null) {
            return null;
        }
        vo.setKey(entity.getKey());
        vo.setFieldCode(entity.getKey());
        vo.setSort(entity.getSort());
        vo.setFieldName(StrUtil.trim(vo.getFieldName()));
        if (vo.getOptionList() == null) {
            vo.setOptionList(new ArrayList<>());
        }
        if(vo.getImageList() == null){
            vo.setImageList(new ArrayList<>());
        }
        return vo;
    }

    /**
     * 删除自定义字段配置
     *
     * @param key 配置key
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String key) {
        String trimKey = StrUtil.trim(key);
        ServiceException.isTrue(StrUtil.isNotBlank(trimKey), "key不能为空");
        LambdaQueryWrapper<FtbParamEntity> deleteWrapper = Wrappers.lambdaQuery();
        deleteWrapper.eq(FtbParamEntity::getType, customerFieldType);
        deleteWrapper.eq(FtbParamEntity::getKey, trimKey);
        int deleteCount = baseParamMapper.delete(deleteWrapper);
        ServiceException.isTrue(deleteCount > 0, "自定义字段配置不存在");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void adjustSort(List<String> key) {
        ServiceException.isTrue(CollUtil.isNotEmpty(key), "key list cannot be empty");
        List<String> sortedKeyList = new ArrayList<>();
        Set<String> keySet = new HashSet<>();
        for (String item : key) {
            String trimKey = StrUtil.trim(item);
            ServiceException.isTrue(StrUtil.isNotBlank(trimKey), "key cannot be blank");
            ServiceException.isTrue(keySet.add(trimKey), "key list contains duplicate key");
            sortedKeyList.add(trimKey);
        }

        LambdaQueryWrapper<FtbParamEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(FtbParamEntity::getType, customerFieldType);
        List<FtbParamEntity> entityList = baseParamMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(entityList)) {
            return;
        }

        Map<String, FtbParamEntity> entityMap = entityList.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(item -> StrUtil.trim(item.getKey()), item -> item, (a, b) -> a));
        for (String sortKey : sortedKeyList) {
            ServiceException.isTrue(entityMap.containsKey(sortKey), "自定义字段配置不存在");
        }

        List<FtbParamEntity> remainingEntityList = entityList.stream()
                .filter(item -> !keySet.contains(StrUtil.trim(item.getKey())))
                .sorted((a, b) -> {
                    Long sortA = a.getSort() == null ? Long.MAX_VALUE : a.getSort();
                    Long sortB = b.getSort() == null ? Long.MAX_VALUE : b.getSort();
                    int sortCompare = sortA.compareTo(sortB);
                    if (sortCompare != 0) {
                        return sortCompare;
                    }
                    return StrUtil.nullToDefault(a.getKey(), "").compareTo(StrUtil.nullToDefault(b.getKey(), ""));
                })
                .collect(Collectors.toList());
        for (FtbParamEntity entity : remainingEntityList) {
            sortedKeyList.add(StrUtil.trim(entity.getKey()));
        }

        long targetSort = 1L;
        for (String sortKey : sortedKeyList) {
            FtbParamEntity entity = entityMap.get(sortKey);
            if (entity == null) {
                continue;
            }
            if (!Objects.equals(entity.getSort(), targetSort)) {
                entity.setSort(targetSort);
                baseParamMapper.updateByKey(sortKey, entity);
            }
            targetSort++;
        }
    }

    /**
     * 按key新增或更新
     *
     * 规则：
     * 1. key为空，新增；
     * 2. key不为空，按key更新。
     *
     * @param req 请求参数
     * @return 配置key
     */
    /**
     * 查询自定义名称。
     *
     * 读取 base_param 表中 key=customerNameType 且 type=customerNameType 的记录。
     *
     * @return 自定义名称；未配置时返回空字符串
     */
    @Override
    public String queryCustomName() {
        FtbParamEntity entity = baseParamMapper.selectByKey(customerNameType);
        if (entity == null || !StrUtil.equals(customerNameType, StrUtil.trim(entity.getType()))) {
            return "";
        }
        return StrUtil.blankToDefault(StrUtil.trim(entity.getValue()), "");
    }

    /**
     * 更新自定义名称。
     *
     * 保存逻辑：先按固定key更新，若不存在则插入；type 固定为 customerNameType。
     *
     * @param customName 自定义名称
     * @return 更新后的自定义名称
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updateCustomName(String customName) {
        String name = StrUtil.trim(customName);
        ServiceException.isTrue(StrUtil.isNotBlank(name), "customName cannot be blank");

        FtbParamEntity entity = new FtbParamEntity();
        entity.setId(FtbUtil.getId());
        entity.setKey(customerNameType);
        entity.setType(customerNameType);
        entity.setValue(name);
        entity.setSort(0L);

        int updateCount = baseParamMapper.updateByKey(customerNameType, entity);
        if (updateCount > 0) {
            return name;
        }

        try {
            baseParamMapper.insertParam(entity);
        } catch (DuplicateKeyException e) {
            int retryCount = baseParamMapper.updateByKey(customerNameType, entity);
            ServiceException.isTrue(retryCount > 0, "update customName failed");
        }
        return name;
    }

    private String saveOrUpdate(FranchiseeCustomFieldConfigReq req) {
        validateReq(req);
        String key = StrUtil.trim(req.getKey());
        if (StrUtil.isBlank(key)) {
            key = FtbUtil.getId();
            FtbParamEntity entity = buildEntity(key, req);
            entity.setId(FtbUtil.getId());
            insertCustomerField(entity);
            return key;
        }

        FtbParamEntity source = baseParamMapper.selectByKey(key);
        ServiceException.notNull(source, "自定义字段配置不存在");
        ServiceException.isTrue(customerFieldType.equals(source.getType()), "自定义字段配置不存在");
        int updateCount = baseParamMapper.updateByKey(key, buildEntity(key, req));
        ServiceException.isTrue(updateCount > 0, "更新失败");
        return key;
    }

    private void insertCustomerField(FtbParamEntity entity) {
        String lockKey = buildFranchiseeCustomerFieldAddLock();
        RLock rLock = redissonClient.getLock(lockKey);
        try {
            rLock.lock(10, TimeUnit.SECONDS);
            Long count = baseParamMapper.selectCount(new LambdaQueryWrapper<FtbParamEntity>()
                    .eq(FtbParamEntity::getType, customerFieldType));
            if(count >= 100){
                throw new ServiceException("自定义字段配置已满100");
            }
            baseParamMapper.insertParam(entity);
        }finally {
            rLock.unlock();
        }
    }

    /**
     * 构建并序列化配置实体
     *
     * @param key 配置key
     * @param req 请求参数
     * @return BaseParam实体
     */
    private FtbParamEntity buildEntity(String key, FranchiseeCustomFieldConfigReq req) {
        FranchiseeCustomFieldConfigValueModel saveData = buildSaveData(key, req);
        FtbParamEntity entity = new FtbParamEntity();
        entity.setKey(key);
        entity.setType(customerFieldType);
        entity.setSort(req.getSort() == null ? 0L : req.getSort());
        entity.setValue(JsonUtil.getObjectToString(saveData));
        return entity;
    }

    /**
     * 构建保存数据对象
     *
     * @param key 配置key
     * @param req 请求参数
     * @return 保存对象
     */
    private FranchiseeCustomFieldConfigValueModel buildSaveData(String key, FranchiseeCustomFieldConfigReq req) {
        FranchiseeCustomFieldConfigValueModel vo = new FranchiseeCustomFieldConfigValueModel();
        vo.setFieldCode(key);
        vo.setFieldName(StrUtil.trim(req.getFieldName()));
        vo.setFieldType(req.getFieldType());
        vo.setRequiredMark(req.getRequiredMark() != null && req.getRequiredMark());
        vo.setSort(req.getSort() == null ? 0L : req.getSort());
        List<FranchiseeCustomFieldConfigOptionVO> optionList = req.getOptionList().stream()
                .filter(Objects::nonNull)
                .map(item -> {
                    FranchiseeCustomFieldConfigOptionVO option = new FranchiseeCustomFieldConfigOptionVO();
                    option.setOptionName(StrUtil.trim(item.getOptionName()));
                    return option;
                })
                .filter(item -> StrUtil.isNotBlank(item.getOptionName()))
                .collect(Collectors.toList());
        vo.setOptionList(optionList);

        List<FranchiseeCustomFieldConfigOptionVO> imageList = req.getImageList().stream()
                .filter(Objects::nonNull)
                .map(item -> {
                    FranchiseeCustomFieldConfigOptionVO option = new FranchiseeCustomFieldConfigOptionVO();
                    option.setOptionName(StrUtil.trim(item.getOptionName()));
                    return option;
                })
                .filter(item -> StrUtil.isNotBlank(item.getOptionName()))
                .collect(Collectors.toList());
        vo.setImageList(imageList);
        vo.setTextTips(req.getTextTips());
        return vo;
    }

    /**
     * 校验请求参数
     *
     * @param req 请求参数
     */
    private void validateReq(FranchiseeCustomFieldConfigReq req) {
        ServiceException.notNull(req, "请求参数不能为空");
        ServiceException.isTrue(StrUtil.isNotBlank(req.getFieldName()), "字段名称不能为空");
        ServiceException.notNull(req.getFieldType(), "字段类型不能为空");
        if (req.getOptionList() == null) {
            req.setOptionList(new ArrayList<>());
        }
        if (FranchiseeCustomFieldTypeEnum.MULTI_OPTION.equals(req.getFieldType())) {
            ServiceException.isTrue(CollUtil.isNotEmpty(req.getOptionList()), "选项不能为空");
        }
        for (FranchiseeCustomFieldConfigOptionReq option : req.getOptionList()) {
            ServiceException.notNull(option, "选项不能为空");
            ServiceException.isTrue(StrUtil.isNotBlank(option.getOptionName()), "选项名称不能为空");
        }
    }

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

    /**
     * 保存到BaseParamEntity.value的模型（不包含key）
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private static class FranchiseeCustomFieldConfigValueModel {
        private String fieldCode;
        private String fieldName;
        private FranchiseeCustomFieldTypeEnum fieldType;
        private List<FranchiseeCustomFieldConfigOptionVO> optionList = new ArrayList<>();
        private List<FranchiseeCustomFieldConfigOptionVO> imageList = new ArrayList<>();
        private Boolean requiredMark;
        private Long sort;
        private String textTips;
    }
}
