package com.crm.pay.service.impl;

import com.alibaba.fastjson.JSON;
import com.crm.pay.dao.repository.SysRemitChannelRepository;
import com.crm.pay.service.FinanceRateService;
import com.crm.pay.service.SysConfigService;
import com.crm.pay.service.SysRemitChannelService;
import com.crm.rely.backend.core.constant.Constants;
import com.crm.rely.backend.core.constant.ManagerConstant;
import com.crm.rely.backend.core.dto.base.BaseResultDto;
import com.crm.rely.backend.core.dto.base.PageDto;
import com.crm.rely.backend.core.dto.base.ResultWithPagerDto;
import com.crm.rely.backend.core.entity.base.SingleLongEntity;
import com.crm.rely.backend.core.pojo.table.SysConfigTable;
import com.crm.rely.backend.core.exception.ServiceException;
import com.crm.rely.backend.model.constant.ConfigConstants;
import com.crm.rely.backend.model.constant.PayConstants;
import com.crm.rely.backend.model.dto.sys.remit.SysRemitChannelApiDto;
import com.crm.rely.backend.model.dto.sys.remit.SysRemitChannelDto;
import com.crm.rely.backend.model.dto.sys.remit.SysRemitChannelGetCodeDto;
import com.crm.rely.backend.model.entity.sys.config.SysConfigFinanceEntity;
import com.crm.rely.backend.model.entity.sys.remit.*;
import com.crm.rely.backend.model.pojo.table.SysRemitChannelTable;
import com.crm.rely.backend.util.FormatPage;
import com.google.common.base.Strings;
import lombok.extern.log4j.Log4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.util.*;

@Log4j
@Service
public class SysRemitChannelServiceImpl implements SysRemitChannelService {

    @Autowired
    private SysRemitChannelRepository sysRemitChannelRepository;
    @Autowired
    private FinanceRateService financeRateService;
    @Autowired
    private SysConfigService sysConfigService;

    private ThreadLocal<BigDecimal> maxAmount = new ThreadLocal<>();

    private ThreadLocal<BigDecimal> minAmount = new ThreadLocal<>();


    @Override
    public BaseResultDto<List<SysRemitChannelDto>> getAllList(SysRemitChannelListEntity entity) throws Exception {
        List<SysRemitChannelTable> remitChannelTables;

        if (entity.getChannelCode() == null || entity.getChannelCode().size() <= 0) {
            remitChannelTables = sysRemitChannelRepository.getAllByValidOrderBySubIndex(1);
        } else {
            remitChannelTables = sysRemitChannelRepository.getAllByValidAndCodeInOrderBySubIndex(1,
                    entity.getChannelCode());
        }
        if (remitChannelTables == null || remitChannelTables.size() <= 0) {
            return BaseResultDto.success();
        }
        getValidatedAmount();

        List<SysRemitChannelDto> sysRemitChannelDtos = new ArrayList<>(remitChannelTables.size());
        for (SysRemitChannelTable sysRemitChannelTable : remitChannelTables) {

            if (sysRemitChannelTable.getMaxAmount() == null) {
                sysRemitChannelTable.setMaxAmount(maxAmount.get());
            }
            if (sysRemitChannelTable.getMinAmount() == null) {
                sysRemitChannelTable.setMinAmount(minAmount.get());
            }

            SysRemitChannelDto sysRemitChannelDto = sysRemitChannelTable.transferToDto(SysRemitChannelDto.class);

            if ((sysRemitChannelTable.getBankValid() != null && !sysRemitChannelTable.getBankValid().equals(1) &&
                    sysRemitChannelTable.getCurrency() != null && sysRemitChannelTable.getTransformCurrency() != null)) {

                try {
                    BigDecimal rate = financeRateService.getRate(sysRemitChannelTable.getCurrency(),
                            sysRemitChannelTable.getTransformCurrency(), 1);
                    sysRemitChannelDto.setRate(rate);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }

            }
            BigDecimal rate = null;
            try {
                if (PayConstants.CHANNEL_TYPE_BANK.equals(sysRemitChannelTable.getCode()) || PayConstants.CHANNEL_TYPE_BANK_TELEGRAPHIC.equals(sysRemitChannelTable.getCode()) || (sysRemitChannelTable.getBankValid() != null && !sysRemitChannelTable.getBankValid().equals(1)
                        && sysRemitChannelTable.getCurrency() != null && sysRemitChannelTable.getTransformCurrency() != null)) {
                    try {
                        if (sysRemitChannelTable.getRate() != null) {
                            rate = sysRemitChannelTable.getRate();
                        } else {
                            rate = financeRateService.getRate(sysRemitChannelTable.getCurrency(),
                                    sysRemitChannelTable.getTransformCurrency(), 1);
                        }

                    } catch (Exception e) {
                        log.error(e.getMessage());
                    }
                }
            } finally {
                sysRemitChannelDto.setRate(rate);
            }
            sysRemitChannelDtos.add(sysRemitChannelDto);

        }
        return BaseResultDto.success(sysRemitChannelDtos);
    }


    /**
     * 根据实体查询通道 并返回值
     *
     * @param entity
     * @return
     * @throws Exception
     */
    @Override
    public BaseResultDto<SysRemitChannelDto> get(SysRemitChannelByCodeEntity entity) throws Exception {

        SysRemitChannelDto sysRemittanceChannelDto = getByCode(entity.getCode());
        if (sysRemittanceChannelDto == null) {
            return BaseResultDto.error();
        }
        return BaseResultDto.success(sysRemittanceChannelDto);
    }

    @Override
    public List<SysRemitChannelGetCodeDto> getCodeList() throws ServiceException {

        List<SysRemitChannelTable> tables = sysRemitChannelRepository.getAllByIdNotNull();

        List<SysRemitChannelGetCodeDto> transforms = transformGetCode(tables);
        if (transforms == null || transforms.size() <= 0) {
            return null;
        }
        return transforms;
    }

    /**
     * 根据code获取通道返回数据
     *
     * @param code
     * @return
     * @throws Exception
     */
    @Override
    public SysRemitChannelDto getByCode(String code) throws Exception {
        SysRemitChannelTable sysRemitChannelTable = getSysRemitChannelByCode(code);
        if (sysRemitChannelTable == null) {
            return null;
        }

        SysRemitChannelDto sysRemitChannelDto = transform(sysRemitChannelTable);

        return sysRemitChannelDto;
    }

    /**
     * 获取通道配置实体
     *
     * @param code
     * @return
     * @throws Exception
     */
    @Override
    public SysRemitChannelTable getSysRemitChannelByCode(String code) throws ServiceException {
        SysRemitChannelTable sysRemitChannelTable = sysRemitChannelRepository.getFirstByCodeAndValid(code, 1);
        if (sysRemitChannelTable == null) {
            return null;
        }

        return sysRemitChannelTable;

    }

    @Override
    public SysRemitChannelTable getRemitChannelByCode(String code) throws ServiceException {
        SysRemitChannelTable sysRemitChannelTable = sysRemitChannelRepository.getFirstByCode(code);
        if (sysRemitChannelTable == null) {
            return null;
        }
        return sysRemitChannelTable;

    }


    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void save(SysRemitChannelBatchUpdateEntity entity, List<SysRemitChannelBaseUpdateEntity> entitys) throws ServiceException {

        List<SysRemitChannelTable> sysRemitChannelTables = sysRemitChannelRepository.findAll();

        if (entitys == null || entitys.size() <= 0) {
            sysRemitChannelRepository.delete(sysRemitChannelTables);
        }
        Map<String, String> mapCurrency = new HashMap<>(sysRemitChannelTables.size());
        Map<Long, SysRemitChannelTable> mapId = new HashMap<>(sysRemitChannelTables.size());
        for (SysRemitChannelTable sysRemitChannelTable : sysRemitChannelTables) {

            mapId.put(sysRemitChannelTable.getId(), sysRemitChannelTable);
        }

        List<SysRemitChannelTable> sysRemitChannelSaveTables = new ArrayList<>(entitys.size());
        int size = entitys.size();
        for (int i = 0; i < size; i++) {
            SysRemitChannelBaseUpdateEntity financeRateUpdateEntity = entitys.get(i);

            String key = financeRateUpdateEntity.getCode();
            if (mapCurrency.containsKey(key)) {
                throw ServiceException.exception(ManagerConstant.REMIT_CHANNEL_CODE_EXIST);
            } else {
                mapCurrency.put(key, key);
            }
            SysRemitChannelTable sysRemitChannelTable;
            if (financeRateUpdateEntity.getId() != null) {
                sysRemitChannelTable = mapId.get(financeRateUpdateEntity.getId());
                if (sysRemitChannelTable == null) {
                    throw new ServiceException(Constants.INFO_NOT_FOUND);
                }

                BeanUtils.copyProperties(financeRateUpdateEntity, sysRemitChannelTable);

                mapId.remove(sysRemitChannelTable.getId());
            } else {
                sysRemitChannelTable = new SysRemitChannelTable();
                BeanUtils.copyProperties(financeRateUpdateEntity, sysRemitChannelTable);
                sysRemitChannelTable.setAddIp(financeRateUpdateEntity.getModifyIp());
                sysRemitChannelTable.setAddUser(financeRateUpdateEntity.getModifyUser());
                sysRemitChannelTable.setAddTime(financeRateUpdateEntity.getModifyTime());
                sysRemitChannelTable.setModifyTime(null);
                sysRemitChannelTable.setModifyIp(null);
                sysRemitChannelTable.setModifyUser(null);


            }
            sysRemitChannelSaveTables.add(sysRemitChannelTable);
        }

        if (sysRemitChannelSaveTables != null && !sysRemitChannelSaveTables.isEmpty()) {
            sysRemitChannelRepository.save(sysRemitChannelSaveTables);
        }


        if (!mapId.isEmpty()) {
            Collection<SysRemitChannelTable> remitChannelTables = mapId.values();

            List<String> codes = new ArrayList<>(remitChannelTables.size());
            for (SysRemitChannelTable sysRemitChannelTable :
                    remitChannelTables) {
                codes.add(sysRemitChannelTable.getCode());
            }
            sysRemitChannelRepository.delete(remitChannelTables);
        }

    }

    @Override
    public ResultWithPagerDto<SysRemitChannelDto> searchPageList(SysRemitChannelSearchEntity entity) throws ServiceException {
        //创建有序map map为排序字段
        Map<String, String> map = new LinkedHashMap<>(1);
        map.put("subIndex", "asc");
        map.put("addTime", "desc");
        Pageable pageable = FormatPage.formatPageable(entity.getPage(), map);

        Specification specification = getSpecification(entity);

        Page<SysRemitChannelTable> sysRemitChannelTables = sysRemitChannelRepository.findAll(specification, pageable);

        long count = sysRemitChannelTables.getTotalElements();
        PageDto pageDto = PageDto.format(entity.getPage(), count);

        if (count <= 0) {
            return ResultWithPagerDto.success(pageDto);
        }

        List<SysRemitChannelDto> transform = transform(sysRemitChannelTables.getContent());

        return ResultWithPagerDto.success(pageDto, transform);
    }

    @Override
    public List<SysRemitChannelDto> searchList() throws ServiceException {

        List<SysRemitChannelTable> sysRemitChannelTables = sysRemitChannelRepository.findAll();

        List<SysRemitChannelDto> transforms = transform(sysRemitChannelTables);

        if (transforms == null || transforms.size() <= 0) {
            return null;
        }

        return transforms;
    }

    @Override
    public SysRemitChannelDto searchSingle(SingleLongEntity entity) throws ServiceException {
        SysRemitChannelTable sysRemitChannelTable = sysRemitChannelRepository.getFirstById(entity.getId());
        if (sysRemitChannelTable == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        SysRemitChannelDto transform = transform(sysRemitChannelTable);
        return transform;
    }

    @Override
    public List<SysRemitChannelApiDto> searchWithdrawApi() throws ServiceException {
        List<SysRemitChannelTable> sysRemitChannelTables = sysRemitChannelRepository.getAllByRemitApi(1);
        if (sysRemitChannelTables == null) {
            return null;
        }
        List<SysRemitChannelApiDto> dtos = new ArrayList<>(sysRemitChannelTables.size());
        for (SysRemitChannelTable sysRemitChannelTable : sysRemitChannelTables) {
            SysRemitChannelApiDto apiDto = new SysRemitChannelApiDto();
            BeanUtils.copyProperties(sysRemitChannelTable, apiDto);
            dtos.add(apiDto);
        }
        return dtos;
    }

    private Specification getSpecification(SysRemitChannelSearchEntity entity) {

        Specification<SysRemitChannelTable> specification = (root, query, cb) -> {

            List<Predicate> predicates = new ArrayList<>(5);
            if (!Strings.isNullOrEmpty(entity.getName())) {
                predicates.add(cb.equal(root.get("name").as(String.class), entity.getName()));
            }

            if (!Strings.isNullOrEmpty(entity.getCode())) {
                predicates.add(cb.equal(root.get("code").as(String.class), entity.getCode()));
            }
            if (entity.getValid() != null) {
                predicates.add(cb.equal(root.get("valid").as(String.class), entity.getValid()));
            }

            Predicate[] pre = new Predicate[predicates.size()];

            return query.where(predicates.toArray(pre)).getRestriction();
        };

        return specification;
    }

    private List<SysRemitChannelDto> transform(List<SysRemitChannelTable> sysRemitChannelTables) throws ServiceException {

        if (sysRemitChannelTables == null || sysRemitChannelTables.size() <= 0) {
            return null;
        }
        SysConfigFinanceEntity sysConfigFinanceEntity = getFinanceConfigure();
        return transform(sysRemitChannelTables, sysConfigFinanceEntity);
    }

    private List<SysRemitChannelDto> transform(List<SysRemitChannelTable> sysRemitChannelTables, SysConfigFinanceEntity sysConfigFinanceEntity) {

        if (sysRemitChannelTables == null || sysRemitChannelTables.size() <= 0) {
            return null;
        }

        List<SysRemitChannelDto> sysRemitChannelSearchDtos = new ArrayList<>(sysRemitChannelTables.size());
        for (SysRemitChannelTable sysRemitChannelTable : sysRemitChannelTables) {
            sysRemitChannelSearchDtos.add(transform(sysRemitChannelTable, sysConfigFinanceEntity));
        }

        return sysRemitChannelSearchDtos;
    }

    private SysRemitChannelDto transform(SysRemitChannelTable channelTable) throws ServiceException {
        SysConfigFinanceEntity sysConfigFinanceEntity = getFinanceConfigure();
        return transform(channelTable, sysConfigFinanceEntity);
    }

    private SysRemitChannelDto transform(SysRemitChannelTable channelTable, SysConfigFinanceEntity sysConfigFinanceEntity) {
        SysRemitChannelDto sysRemitChannelSearchDto = new SysRemitChannelDto();
        BeanUtils.copyProperties(channelTable, sysRemitChannelSearchDto);
        sysRemitChannelSearchDto.setMinAmount(sysConfigFinanceEntity.getMinWithdraw());

        sysRemitChannelSearchDto.setRate(sysConfigFinanceEntity.getWithdrawRate());
        return sysRemitChannelSearchDto;
    }

    private void existCode(String code) throws ServiceException {
        boolean exist = sysRemitChannelRepository.existsByCode(code);
        if (exist) {
            throw ServiceException.exception(ManagerConstant.REMITTANCE_CHANNEL_CODE_EXIST);
        }
    }

    private SysConfigFinanceEntity getFinanceConfigure() throws ServiceException {

        SysConfigTable sysConfigTable = sysConfigService.getByCode(ConfigConstants.FINANCE_CONFIGURE);
        SysConfigFinanceEntity financeEntity = JSON.parseObject(sysConfigTable.getValue(), SysConfigFinanceEntity.class);
        if (financeEntity == null) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }
        return financeEntity;
    }

    private void getValidatedAmount() throws ServiceException {
        maxAmount.set(null);
        maxAmount.set(null);
        List<String> codes = new ArrayList<>(2);

        codes.add(ConfigConstants.FINANCE_WITHDRAW_MAX);
        codes.add(ConfigConstants.FINANCE_WITHDRAW_MIN);
        List<SysConfigTable> configTables = sysConfigService.getByCodes(codes);
        if (configTables == null || configTables.size() < 2) {
            throw new ServiceException(Constants.SYSTEM_ERROR);
        }
        for (SysConfigTable configTable : configTables) {

            if (ConfigConstants.FINANCE_WITHDRAW_MAX.equals(configTable.getCode())) {
                try {
                    maxAmount.set(new BigDecimal(configTable.getValue()));
                } catch (NumberFormatException e) {
                    throw new ServiceException(Constants.SYSTEM_ERROR);
                }
            }
            if (ConfigConstants.FINANCE_WITHDRAW_MIN.equals(configTable.getCode())) {
                try {
                    minAmount.set(new BigDecimal(configTable.getValue()));
                } catch (NumberFormatException e) {
                    throw new ServiceException(Constants.SYSTEM_ERROR);
                }
            }
        }
    }

    private List<SysRemitChannelGetCodeDto> transformGetCode(List<SysRemitChannelTable> tables) {

        if (tables == null || tables.size() <= 0) {
            return null;
        }

        List<SysRemitChannelGetCodeDto> dtoArrayList = new ArrayList<>(tables.size());
        tables.forEach(value -> dtoArrayList.add(transformGetCode(value)));

        return dtoArrayList;
    }

    private SysRemitChannelGetCodeDto transformGetCode(SysRemitChannelTable table) {
        SysRemitChannelGetCodeDto dto = new SysRemitChannelGetCodeDto();
        BeanUtils.copyProperties(table, dto);
        return dto;
    }
}
