package com.sunyard.chsm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunyard.chsm.dto.CryptoServiceDTO;
import com.sunyard.chsm.enums.ApiFunEnum;
import com.sunyard.chsm.enums.EnableStatus;
import com.sunyard.chsm.mapper.CryptoServiceApiMapper;
import com.sunyard.chsm.mapper.CryptoServiceDeviceGroupMapper;
import com.sunyard.chsm.mapper.CryptoServiceMapper;
import com.sunyard.chsm.mapper.SpDeviceGroupMapper;
import com.sunyard.chsm.model.entity.CryptoService;
import com.sunyard.chsm.model.entity.CryptoServiceApi;
import com.sunyard.chsm.model.entity.CryptoServiceDeviceGroup;
import com.sunyard.chsm.model.entity.DeviceGroup;
import com.sunyard.chsm.service.CryptoServiceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author liulu
 * @since 2024/10/15
 */
@Slf4j
@Service
@Transactional
public class CryptoServiceServiceImpl implements CryptoServiceService {

    @Resource
    private CryptoServiceMapper cryptoServiceMapper;
    @Resource
    private CryptoServiceApiMapper cryptoServiceApiMapper;
    @Resource
    private CryptoServiceDeviceGroupMapper cryptoServiceDeviceGroupMapper;
    @Resource
    private SpDeviceGroupMapper spDeviceGroupMapper;

    @Override
    public Page<CryptoServiceDTO.CSView> selectPageList(CryptoServiceDTO.Query query) {

        LambdaQueryWrapper<CryptoService> wrapper = new LambdaQueryWrapper<CryptoService>()
                .like(StringUtils.hasText(query.getName()), CryptoService::getName, query.getName())
                .orderByDesc(CryptoService::getCreateTime);

        IPage<CryptoService> servicePage = cryptoServiceMapper.selectPage(
                new Page<>(query.getPageNumber(), query.getPageSize()),
                wrapper);
        List<CryptoService> records = servicePage.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            return new Page<>(servicePage.getCurrent(), servicePage.getSize(), servicePage.getTotal());
        }

        List<Long> ids = records.stream().map(CryptoService::getId).collect(Collectors.toList());

        List<CryptoServiceDeviceGroup> dgs = cryptoServiceDeviceGroupMapper.selectByServiceIds(ids);
        Map<Long, List<CryptoServiceDeviceGroup>> serviceDgMap = dgs.stream().collect(Collectors.groupingBy(CryptoServiceDeviceGroup::getServiceId));

        // service ap
        LambdaQueryWrapper<CryptoServiceApi> apiQuery = new LambdaQueryWrapper<CryptoServiceApi>()
                .in(CryptoServiceApi::getCryptoServiceId, ids);
        List<CryptoServiceApi> allServiceApis = cryptoServiceApiMapper.selectList(apiQuery);

        Map<Long, List<CryptoServiceApi>> serviceApiMap = allServiceApis.stream()
                .collect(Collectors.groupingBy(CryptoServiceApi::getCryptoServiceId));

        List<CryptoServiceDTO.CSView> viewList = records.stream()
                .map(it -> {
                    CryptoServiceDTO.CSView view = new CryptoServiceDTO.CSView();
                    BeanUtils.copyProperties(it, view);
                    Optional.ofNullable(EnableStatus.of(it.getStatus()))
                            .map(EnableStatus::getDesc)
                            .ifPresent(view::setStatusDesc);
                    List<CryptoServiceDeviceGroup> dgList = serviceDgMap.getOrDefault(it.getId(), Collections.emptyList());
                    if (!CollectionUtils.isEmpty(dgList)) {
                        CryptoServiceDeviceGroup next = dgList.iterator().next();
                        view.setDeviceGroupId(next.getDeviceGroupId());
                        view.setDeviceGroupName(next.getDeviceGroupName());
                    }

                    List<CryptoServiceApi> serviceApis = serviceApiMap.getOrDefault(it.getId(), Collections.emptyList());
                    List<String> groupCodes = serviceApis.stream().map(CryptoServiceApi::getApiGroup)
                            .distinct().collect(Collectors.toList());
                    List<String> apiList = serviceApis.stream()
                            .map(CryptoServiceApi::getApiCode).collect(Collectors.toCollection(ArrayList::new));
                    apiList.addAll(groupCodes);
                    view.setApiList(apiList);

                    String apiDesc = serviceApis.stream()
                            .map(CryptoServiceApi::getApiName).collect(Collectors.joining(","));
                    view.setApiDesc(apiDesc);

                    return view;
                })
                .collect(Collectors.toList());

        return new Page<CryptoServiceDTO.CSView>(servicePage.getCurrent(),
                servicePage.getSize(), servicePage.getTotal()).setRecords(viewList);
    }

    @Override
    public Long save(CryptoServiceDTO.CSSave save) {

        checkName(save.getName());

        CryptoService service = new CryptoService();
        service.setId(IdWorker.getId());
        service.setName(save.getName());

        DeviceGroup deviceGroup = spDeviceGroupMapper.selectById(save.getDeviceGroupId());
        Assert.notNull(deviceGroup, "设备组不存在");

        CryptoServiceDeviceGroup csDg = new CryptoServiceDeviceGroup();
        csDg.setId(IdWorker.getId());
        csDg.setServiceId(service.getId());
        csDg.setDeviceGroupId(deviceGroup.getId());
        csDg.setDeviceGroupName(deviceGroup.getName());
        cryptoServiceDeviceGroupMapper.insert(csDg);

//        service.setDeviceGroupId(save.getDeviceGroupId());
//        service.setDeviceGroupName(deviceGroup.getName());

        service.setStatus(EnableStatus.ENABLED.getCode());
        service.setRemark(save.getRemark());

        cryptoServiceMapper.insert(service);

        // save apis
        saveApis(save.getApiList(), service.getId());
        return service.getId();
    }

    @Override
    public void update(CryptoServiceDTO.CSSave update) {
        CryptoService exist = cryptoServiceMapper.selectById(update.getId());
        Assert.notNull(exist, "密码服务不存在");
        if (!Objects.equals(update.getName(), exist.getName())) {
            checkName(update.getName());
        }
        CryptoService up = new CryptoService();
        up.setId(update.getId());
        up.setName(update.getName());

        CryptoServiceDeviceGroup existDg = cryptoServiceDeviceGroupMapper.selectByServiceId(exist.getId());
        if (existDg == null || !Objects.equals(update.getDeviceGroupId(), existDg.getDeviceGroupId())) {
            DeviceGroup deviceGroup = spDeviceGroupMapper.selectById(update.getDeviceGroupId());
            Assert.notNull(deviceGroup, "设备组不存在");

            cryptoServiceDeviceGroupMapper.deleteByServiceId(exist.getId());
            CryptoServiceDeviceGroup csDg = new CryptoServiceDeviceGroup();
            csDg.setId(IdWorker.getId());
            csDg.setServiceId(exist.getId());
            csDg.setDeviceGroupId(deviceGroup.getId());
            csDg.setDeviceGroupName(deviceGroup.getName());
            cryptoServiceDeviceGroupMapper.insert(csDg);
//            up.setDeviceGroupId(update.getDeviceGroupId());
//            up.setDeviceGroupName(deviceGroup.getName());
        }

        up.setRemark(update.getRemark());
        up.setUpdateTime(LocalDateTime.now());
        cryptoServiceMapper.updateById(up);

        LambdaQueryWrapper<CryptoServiceApi> wrapper = new LambdaQueryWrapper<CryptoServiceApi>()
                .eq(CryptoServiceApi::getCryptoServiceId, exist.getId());
        cryptoServiceApiMapper.delete(wrapper);

        // save apis
        saveApis(update.getApiList(), exist.getId());
    }

    @Override
    public void enable(Long id) {
        CryptoService cs = cryptoServiceMapper.selectById(id);
        Assert.notNull(cs, "密码服务不存在");
        Assert.isTrue(Objects.equals(EnableStatus.DISABLED.getCode(), cs.getStatus()),
                "当前密码服务不是停用状态,不支持启用");

        CryptoService upCs = new CryptoService();
        upCs.setId(id);
        upCs.setStatus(EnableStatus.ENABLED.getCode());
        upCs.setUpdateTime(LocalDateTime.now());
        cryptoServiceMapper.updateById(upCs);
    }

    @Override
    public void disable(Long id) {
        CryptoService cs = cryptoServiceMapper.selectById(id);
        Assert.notNull(cs, "密码服务不存在");
        Assert.isTrue(Objects.equals(EnableStatus.ENABLED.getCode(), cs.getStatus()),
                "当前密码服务不是停用状态,不支持启用");

        CryptoService upCs = new CryptoService();
        upCs.setId(id);
        upCs.setStatus(EnableStatus.DISABLED.getCode());
        upCs.setUpdateTime(LocalDateTime.now());
        cryptoServiceMapper.updateById(upCs);
    }

    @Override
    public void delete(Long id) {
        CryptoService exist = cryptoServiceMapper.selectById(id);
        Assert.notNull(exist, "密码服务不存在");

        Assert.isTrue(Objects.equals(EnableStatus.DISABLED.getCode(), exist.getStatus()),
                "密码服务为启用状态, 无法删除");

        LambdaQueryWrapper<CryptoServiceApi> wrapper = new LambdaQueryWrapper<CryptoServiceApi>()
                .eq(CryptoServiceApi::getCryptoServiceId, id);
        cryptoServiceApiMapper.delete(wrapper);
        cryptoServiceDeviceGroupMapper.deleteByServiceId(id);
        cryptoServiceMapper.deleteById(id);
    }

    private void checkName(String name) {
        LambdaQueryWrapper<CryptoService> wrapper = new LambdaQueryWrapper<CryptoService>()
                .eq(CryptoService::getName, name);
        CryptoService exist = cryptoServiceMapper.selectOne(wrapper);
        Assert.isNull(exist, "密码服务名称已存在");
    }

    private void saveApis(List<String> apiList, Long serviceId) {

        Arrays.stream(ApiFunEnum.values())
                .filter(it -> apiList.contains(it.getCode()))
                .map(it -> {
                    CryptoServiceApi serviceApi = new CryptoServiceApi();
                    serviceApi.setId(IdWorker.getId());
                    serviceApi.setCryptoServiceId(serviceId);
                    serviceApi.setApiGroup(it.getGroup().getCode());
                    serviceApi.setApiCode(it.getCode());
                    serviceApi.setApiName(it.getName());
                    return serviceApi;
                })
                .collect(Collectors.toList())
                .forEach(cryptoServiceApiMapper::insert);
    }

}
