package com.fzm.mall.server.admin.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fzm.mall.server.admin.constant.MallResponseError;
import com.fzm.mall.server.admin.exception.MyException;
import com.fzm.mall.server.admin.system.entity.dto.SysBlockChainManagementDTO;
import com.fzm.mall.server.admin.system.entity.dto.SysBlockChainManagementSaveDTO;
import com.fzm.mall.server.admin.system.entity.dto.SysBlockChainManagementUpdateDTO;
import com.fzm.mall.server.admin.system.entity.dto.SysBlockChainPassDTO;
import com.fzm.mall.server.admin.system.mapper.SysBlockChainManagementMapper;
import com.fzm.mall.server.admin.system.model.SysBlockChainManagement;
import com.fzm.mall.server.admin.system.model.SysBlockChainPass;
import com.fzm.mall.server.admin.system.service.ISysBlockChainManagementService;
import com.fzm.mall.server.admin.system.service.ISysBlockChainPassService;
import com.fzm.mall.server.admin.util.CopyUtil;
import org.apache.commons.collections.CollectionUtils;
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 java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统中心-区块链管理 服务实现类
 * </p>
 *
 * @author fzm
 * @since 2021-08-12
 */
@Service
@Transactional
public class SysBlockChainManagementServiceImpl extends ServiceImpl<SysBlockChainManagementMapper, SysBlockChainManagement> implements ISysBlockChainManagementService {

    @Autowired
    private SysBlockChainManagementMapper managementMapper;

    @Autowired
    private ISysBlockChainPassService passService;

    @Override
    public void save(SysBlockChainManagementSaveDTO saveDTO) {
        SysBlockChainManagement management = CopyUtil.to(saveDTO, SysBlockChainManagement.class);
        management.init();

        checkUnique(management.getIp(), management.getPort(), management.getTitle(), management.getBlockChainId());

        managementMapper.insert(management);
    }

    @Override
    public void update(SysBlockChainManagementUpdateDTO updateDTO) {
        SysBlockChainManagement management = CopyUtil.to(updateDTO, SysBlockChainManagement.class);
        checkUnique(management.getIp(), management.getPort(), management.getTitle(), management.getBlockChainId());

        UpdateWrapper<SysBlockChainManagement> wrapper = new UpdateWrapper<>();
        wrapper.eq("block_chain_id", management.getBlockChainId())
                .eq("update_time", management.getUpdateTime());
        int update = managementMapper.update(management, wrapper);
        if (update == 0) {
            throw new MyException(MallResponseError.UPDATE_FAIL);
        }
    }

    private void checkUnique(String ip, String port, String title, String blockChainId) {
        chckIpUnique(ip, port, blockChainId);
        checkTitleUnique(title, blockChainId);
    }

    private void chckIpUnique(String ip, String port, String blockChainId) {
        QueryWrapper<SysBlockChainManagement> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("ip", ip)
                .eq("port", port)
                .eq("status", SysBlockChainManagement.STATUS_EFFECTIVE);
        SysBlockChainManagement management = managementMapper.selectOne(queryWrapper);
        if (management != null && !management.getBlockChainId().equals(blockChainId)) {
            throw new MyException(MallResponseError.SAVE_DUPLICATE);
        }
    }

    private void checkTitleUnique(String title, String blockChainId) {
        if (StringUtils.isBlank(title)) {
            return;
        }

        QueryWrapper<SysBlockChainManagement> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("title", title)
                .eq("status", SysBlockChainManagement.STATUS_EFFECTIVE);
        SysBlockChainManagement management = managementMapper.selectOne(queryWrapper);
        if (management != null && !management.getBlockChainId().equals(blockChainId)) {
            throw new MyException(MallResponseError.SAVE_DUPLICATE);
        }
    }

    @Override
    public void validStateUpdate(String blockChainId, Integer validState, Long updateTime) {
        SysBlockChainManagement management = readById(blockChainId);
        checkState(management);

        UpdateWrapper<SysBlockChainManagement> wrapper = new UpdateWrapper<>();
        wrapper.eq("block_chain_id", blockChainId)
                .eq("update_time", updateTime);
        management.setStatus(validState);
        int update = managementMapper.update(management, wrapper);
        if (update == 0) {
            throw new MyException(MallResponseError.UPDATE_FAIL);
        }
    }

    private void checkState(SysBlockChainManagement management) {
        if (management == null || SysBlockChainManagement.STATUS_DELETED.equals(management.getStatus())) {
            throw new MyException(MallResponseError.UPDATE_FAIL);
        }
    }

    private SysBlockChainManagement readById(String blockChainId) {
        QueryWrapper<SysBlockChainManagement> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("block_chain_id", blockChainId);
        return managementMapper.selectOne(queryWrapper);
    }

    @Override
    public void delete(String blockChainId) {
        SysBlockChainManagement management = readById(blockChainId);

        checkManagementBeforeDelete(management);

        UpdateWrapper<SysBlockChainManagement> wrapper = new UpdateWrapper<>();
        wrapper.eq("block_chain_id", blockChainId)
        ;
        management.setStatus(SysBlockChainManagement.STATUS_DELETED);
        managementMapper.update(management, wrapper);
    }

    private void checkManagementBeforeDelete(SysBlockChainManagement management) {
        if (management == null) {
            return;
        }
        /**
         * 只有停用才可被删除
         */
        if (management.getStatus() != SysBlockChainManagement.STATUS_DISABLED) {
            throw new MyException(MallResponseError.DELETE_FAIL);
        }

        /**
         * 校验链上是否有可用通证
         */
        QueryWrapper<SysBlockChainPass> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("block_chain_id", management.getBlockChainId())
                .in("status", Arrays.asList(SysBlockChainPass.PASS_STATUS_DISABLED, SysBlockChainPass.PASS_STATUS_EFFECTIVE));
        List<SysBlockChainPass> chainPassList = passService.list(queryWrapper);
        if (CollectionUtils.isNotEmpty(chainPassList)) {
            throw new MyException(MallResponseError.DELETE_FAIL);
        }
    }

    @Override
    public SysBlockChainManagementDTO read(String blockChainId) {
        SysBlockChainManagement management = readById(blockChainId);
        return CopyUtil.to(management, SysBlockChainManagementDTO.class);
    }

    @Override
    public IPage<SysBlockChainManagementDTO> page(Page<SysBlockChainManagementDTO> page, String keyword) {
        List<SysBlockChainManagementDTO> list = managementMapper.list(keyword);
        list.forEach(dto -> {
            List<SysBlockChainPassDTO> passList = dto.getPassList();
            List<String> passIdList = passList.stream().filter(pass -> pass.getStatus() != 0)
                    .map(SysBlockChainPassDTO::getPassId).collect(Collectors.toList());
            dto.setPassIdList(passIdList);
        });
        return getPageResult(list, page);
    }

    private IPage<SysBlockChainManagementDTO> getPageResult(List<SysBlockChainManagementDTO> list, Page<SysBlockChainManagementDTO> page) {

        IPage<SysBlockChainManagementDTO> result = new Page<>(page.getCurrent(), page.getSize(), list.size());

        int currentPage = (int) result.getCurrent();
        int pageSize = (int) result.getSize();
        int total = list.size();
        int pages = (int) result.getPages();
        //当前第几页数据
        currentPage = pages < currentPage ? pages : currentPage;
        //起始索引
        int fromIndex = pageSize * (currentPage - 1) < 0 ? 0 : pageSize * (currentPage - 1);
        //结束索引
        int toIndex = pageSize * currentPage > total ? total : pageSize * currentPage;

        list = list.subList(fromIndex, toIndex);
        result.setRecords(list);
        return result;
    }

}
