package com.gitee.sop.admin.service.isv;

import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.LambdaQuery;
import com.gitee.sop.admin.common.constants.YesOrNo;
import com.gitee.sop.admin.common.context.SpringContext;
import com.gitee.sop.admin.common.dto.StatusUpdateDTO;
import com.gitee.sop.admin.common.enums.StatusEnum;
import com.gitee.sop.admin.common.support.ServiceSupport;
import com.gitee.sop.admin.common.util.CopyUtil;
import com.gitee.sop.admin.common.util.IdGen;
import com.gitee.sop.admin.common.util.RSATool;
import com.gitee.sop.admin.dao.entity.IsvInfo;
import com.gitee.sop.admin.dao.entity.IsvKeys;
import com.gitee.sop.admin.dao.mapper.IsvInfoMapper;
import com.gitee.sop.admin.service.isv.dto.IsvInfoAddDTO;
import com.gitee.sop.admin.service.isv.dto.IsvInfoDTO;
import com.gitee.sop.admin.service.isv.dto.IsvInfoUpdateDTO;
import com.gitee.sop.admin.service.isv.dto.IsvInfoUpdateKeysDTO;
import com.gitee.sop.admin.service.isv.dto.IsvKeysDTO;
import com.gitee.sop.admin.service.isv.dto.SetMerchantDTO;
import com.gitee.sop.admin.service.isv.event.ChangeIsvInfoEvent;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * @author 六如
 */
@Service
public class IsvInfoService implements ServiceSupport<IsvInfo, IsvInfoMapper> {

    @Autowired
    private IsvKeysService isvKeysService;
    @Autowired
    private PermIsvGroupService permIsvGroupService;
    @Autowired
    private IsvMerchantService isvMerchantService;

    public PageInfo<IsvInfoDTO> doPage(LambdaQuery<IsvInfo> query) {
        query.orderByDesc(IsvInfo::getId);
        PageInfo<IsvInfo> page = this.page(query);
        List<IsvInfo> list = page.getList();
        if (CollectionUtils.isEmpty(list)) {
            return page.convert(isvInfo -> new IsvInfoDTO());
        }
        List<Long> idList = list.stream()
                .map(IsvInfo::getId).collect(Collectors.toList());

        List<String> appIds = list.stream().map(IsvInfo::getAppId).collect(Collectors.toList());

        Map<Long, IsvKeys> isvIdMap = isvKeysService.query()
                .in(IsvKeys::getIsvId, idList)
                .map(IsvKeys::getIsvId, Function.identity());

        Map<Long, List<String>> isvGroupMap = permIsvGroupService.getIsvGroupNameMap(idList);
        Map<String, String> appIdMerchantMap = isvMerchantService.getAppMerchantCode(appIds);

        // 格式转换
        return page.convert(isvInfo -> {
            IsvInfoDTO isvInfoDTO = CopyUtil.copyBean(isvInfo, IsvInfoDTO::new);
            boolean hasKey = false;
            Optional<IsvKeys> isvKeysOpt = Optional.ofNullable(isvIdMap.get(isvInfo.getId()));
            if (isvKeysOpt.isPresent()) {
                IsvKeys isvKeys = isvKeysOpt.get();
                hasKey = !StringUtils.isAllBlank(
                        isvKeys.getPrivateKeyIsv(),
                        isvKeys.getPrivateKeyPlatform(),
                        isvKeys.getPublicKeyIsv(),
                        isvKeys.getPublicKeyPlatform()
                );
            }
            isvInfoDTO.setHasKeys(YesOrNo.of(hasKey));
            List<String> groupNames = isvGroupMap.getOrDefault(isvInfo.getId(), Collections.emptyList());
            isvInfoDTO.setGroupNames(String.join("/", groupNames));

            String merchantCode = appIdMerchantMap.getOrDefault(isvInfo.getAppId(), "");
            isvInfoDTO.setMerchantCode(merchantCode);
            return isvInfoDTO;
        });
    }

    public RSATool.KeyStore createKeys(RSATool.KeyFormat keyFormat) throws Exception {
        if (keyFormat == null) {
            keyFormat = RSATool.KeyFormat.PKCS8;
        }
        RSATool rsaTool = new RSATool(keyFormat, RSATool.KeyLength.LENGTH_2048);
        return rsaTool.createKeys();
    }

    /**
     * 添加ISV
     *
     * @param isvInfoAddDTO
     * @return 返回id
     */
    @Transactional(rollbackFor = Exception.class)
    public long add(IsvInfoAddDTO isvInfoAddDTO) {
        IsvInfo rec = CopyUtil.copyBean(isvInfoAddDTO, IsvInfo::new);
        String appKey = new SimpleDateFormat("yyyyMMdd").format(new Date()) + IdGen.nextId();
        rec.setAppId(appKey);
        rec.setStatus(StatusEnum.ENABLE.getValue());
        this.save(rec);
        this.sendChangeEvent(rec.getId());
        return rec.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    public int update(IsvInfoUpdateDTO isvInfoUpdateDTO) {
        IsvInfo isvInfo = new IsvInfo();
        isvInfo.setId(isvInfoUpdateDTO.getId());
        isvInfo.setStatus(isvInfoUpdateDTO.getStatus());
        isvInfo.setRemark(isvInfoUpdateDTO.getRemark());
        int cnt = this.update(isvInfo);
        sendChangeEvent(isvInfoUpdateDTO.getId());
        return cnt;
    }

    public int updateKeys(IsvInfoUpdateKeysDTO isvInfoUpdateKeysDTO) {
        return isvKeysService.saveKeys(isvInfoUpdateKeysDTO);
    }

    public IsvKeysDTO getKeys(Long isvId) {
        IsvKeys isvKeys = isvKeysService.get(IsvKeys::getIsvId, isvId);
        IsvKeysDTO isvKeysDTO;
        if (isvKeys == null) {
            isvKeysDTO = new IsvKeysDTO();
            isvKeysDTO.setIsvId(isvId);
            isvKeysDTO.setKeyFormat(RSATool.KeyFormat.PKCS8.getValue());
            isvKeysDTO.setPublicKeyIsv("");
            isvKeysDTO.setPrivateKeyIsv("");
            isvKeysDTO.setPublicKeyPlatform("");
            isvKeysDTO.setPrivateKeyPlatform("");
        } else {
            isvKeysDTO = CopyUtil.copyBean(isvKeys, IsvKeysDTO::new);
        }

        IsvInfo isvInfo = this.getById(isvId);
        isvKeysDTO.setAppId(isvInfo.getAppId());

        return isvKeysDTO;
    }

    /**
     * 修改状态
     *
     * @param statusUpdateDTO 修改值
     * @return 返回影响行数
     */
    @Transactional(rollbackFor = Exception.class)
    public int updateStatus(StatusUpdateDTO statusUpdateDTO) {
        Long isvId = statusUpdateDTO.getId();
        int cnt = this.query()
                .eq(IsvInfo::getId, isvId)
                .set(IsvInfo::getStatus, statusUpdateDTO.getStatus())
                .update();
        // 同步isv信息
        this.sendChangeEvent(isvId);
        return cnt;
    }

    private void sendChangeEvent(Long isvId) {
        SpringContext.publishEvent(new ChangeIsvInfoEvent(Collections.singletonList(isvId)));
    }


    public int setMerchant(SetMerchantDTO setMerchantDTO) {
        Long isvId = setMerchantDTO.getIsvId();
        IsvInfo isvInfo = this.getById(isvId);

        String appId = isvInfo.getAppId();
        int i = isvMerchantService.setMerchant(appId, setMerchantDTO.getMerchantCode());
        sendChangeEvent(isvId);
        return i;
    }
}
