package com.friday.custody.service.basicService.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.friday.custody.dao.FundNetValueInfoDao;
import com.friday.custody.dto.TenantEscrowAuthorizationInfoDto;
import com.friday.custody.entity.FundInfoEntity;
import com.friday.custody.entity.FundNetValueInfoEntity;
import com.friday.custody.enums.RetCodeEnum;
import com.friday.custody.exception.RRException;
import com.friday.custody.req.FundNetValueInfoReq;
import com.friday.custody.req.QueryDataReq;
import com.friday.custody.service.basicService.FundNetValueInfoService;
import com.friday.custody.service.logicService.DataProcessService;
import com.friday.custody.utils.PageUtil;
import com.friday.custody.utils.PageUtils;
import com.friday.custody.utils.Query;
import com.friday.custody.utils.R;
import com.friday.custody.vo.FundNetValueInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


@Service("fundNetValueInfoService")
@Slf4j
public class FundNetValueInfoServiceImpl extends ServiceImpl<FundNetValueInfoDao, FundNetValueInfoEntity>
        implements FundNetValueInfoService, DataProcessService<FundNetValueInfoEntity> {

    @Autowired
    FundNetValueInfoDao fundNetValueInfoDao;
    @Autowired
    private FundInfoServiceImpl fundInfoService;
    @Autowired
    private DataDictionaryZxFndBasicInfoCyServiceImpl zxFndBasicInfoCyService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public R saveData(String type, List<FundNetValueInfoEntity> datas) {
        if (CollectionUtils.isEmpty(datas)){
            return R.ok();
        }
        for (FundNetValueInfoEntity entity : datas) {
            //如果fundCode，fundName为null，innerFundCode不为null则通过innerFundCode查询fundName，fundCode
            if (StringUtils.isBlank(entity.getFundCode()) && StringUtils.isBlank(entity.getFundName()) && StringUtils.isNotBlank(entity.getInnerFundCode())){
                FundInfoEntity fundInfoEntity = fundInfoService.queryByInnerFundCode(entity.getTrustNo(), entity.getAdmin(), entity.getInnerFundCode());
                if (fundInfoEntity != null) {
                    entity.setFundCode(fundInfoEntity.getFundCode());
                    entity.setFundName(fundInfoEntity.getFundName());
                }
            }else {
                //如果fundCode为null，fundName不为null，则通过fundName查询fundCode
                if (StringUtils.isBlank(entity.getFundCode()) && StringUtils.isNotBlank(entity.getFundName())){
                    String fundCode = fundInfoService.getFundCodeByFundName(entity.getFundName(), entity.getTrustNo(), entity.getAdmin());
                    entity.setFundCode(fundCode);
                }
                //如果fundCode不为null，fundName为null，则通过fundCode查询fundName
                if (StringUtils.isNotBlank(entity.getFundCode()) && StringUtils.isBlank(entity.getFundName())){
                    String fundName = fundInfoService.getFundNameByFundCode(entity.getFundCode(), entity.getTrustNo(), entity.getAdmin());
                    entity.setFundName(fundName);
                }
            }

            if (Objects.isNull(entity.getUniqueKey())) {
                if ("36".equals(entity.getTrustNo()) || "30".equals(entity.getTrustNo())) {
                    entity.setUniqueKey(entity.getAdmin() + entity.getInnerFundCode() + entity.getFundName()
                            + entity.getNetValueDate());
                } else if ("33".equals(entity.getTrustNo()) || "41".equals(entity.getTrustNo())) {
                    entity.setUniqueKey(entity.getAdmin() + entity.getTrustNo() + entity.getFundCode() + entity.getNetValueDate());
                } else {
                    entity.setUniqueKey(entity.getAdmin() + entity.getFundCode() + entity.getFundName()
                            + entity.getNetValueDate());
                }
            }
            //1.取fund_net_value_info的数据时，若资产份额、资产净值、资产总计为空或0，落库为：0；
            if (entity.getShares() == null) {
                entity.setShares(BigDecimal.ZERO);
            }
            if (entity.getAssetValue() == null) {
                entity.setAssetValue(BigDecimal.ZERO);
            }
            if (entity.getTotalAsset() == null && !"35".equals(entity.getTrustNo())) {
                entity.setTotalAsset(BigDecimal.ZERO);
            }
            //todo 需要写一个记录失败的表
        }
        //根据主键去重
        List<FundNetValueInfoEntity> uniqueFundNetValueInfoList = datas.stream().collect(Collectors.collectingAndThen
                (Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing
                        (FundNetValueInfoEntity::getUniqueKey))), ArrayList::new));
        List<String> rawUniqueKeyList = uniqueFundNetValueInfoList.stream().map(FundNetValueInfoEntity::getUniqueKey).collect(Collectors.toList());
        String ukList = String.join("--", rawUniqueKeyList);
        if (CollectionUtils.isNotEmpty(rawUniqueKeyList)) {
            Set<String> uniqueKeySet = baseMapper.queryUniqueKeyList(rawUniqueKeyList);
            if (CollectionUtils.isNotEmpty(uniqueKeySet)) {
                baseMapper.deleteByUniqueKeys(uniqueKeySet);
            }

        }
        boolean saveStatus = this.saveBatch(uniqueFundNetValueInfoList);
        if (!saveStatus) {
            log.error("保存基金净值失败:{}", ukList);
        }
        return R.ok();
    }

    private void batchUpdateByUniqueKey(List<FundNetValueInfoEntity> updateList) {
        for (FundNetValueInfoEntity entity : updateList) {
            LambdaUpdateWrapper<FundNetValueInfoEntity> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(FundNetValueInfoEntity::getUniqueKey,entity.getUniqueKey());
            this.update(entity, updateWrapper);
        }
    }

    @Override
    public R queryData(QueryDataReq vo) {
        try {
            log.info("开始查询基金净值信息表...,租户信息为:{}", JSON.toJSONString(vo));
            //1 封装查询参数
            FundNetValueInfoReq fundNetValueInfoReq = JSON.parseObject(vo.getParams(), FundNetValueInfoReq.class);
            //1.1 封装分页查询参数
            Map<String, Object> pageParams = PageUtil.getPageParams(fundNetValueInfoReq);
            //1.2 封装查询参数
            QueryWrapper<FundNetValueInfoEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("admin", vo.getTenantName());
            if (StringUtils.isNotBlank(fundNetValueInfoReq.getFundCode())) {
                queryWrapper.eq("fund_code", fundNetValueInfoReq.getFundCode());
            }
            if (StringUtils.isNotBlank(fundNetValueInfoReq.getFundName())) {
                queryWrapper.likeRight("fund_name", fundNetValueInfoReq.getFundName());
            }
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
            Calendar c = Calendar.getInstance();
            //开始和结束时间都为空，默认为一年
            if (StringUtils.isBlank(fundNetValueInfoReq.getBeginDate())&&StringUtils.isBlank(fundNetValueInfoReq.getEndDate())) {
                c.setTime(new Date());
                c.add(Calendar.YEAR, -1);
                Date m = c.getTime();
                String beginYear = format.format(m);
                fundNetValueInfoReq.setBeginDate(beginYear);
                fundNetValueInfoReq.setEndDate(format.format(new Date()));
            }
            //开始不为空和结束时间为空，默认当天
            if (StringUtils.isBlank(fundNetValueInfoReq.getEndDate())&&StringUtils.isNotBlank(fundNetValueInfoReq.getBeginDate())) {
                c.setTime(format.parse(fundNetValueInfoReq.getBeginDate()));
                c.add(Calendar.YEAR, 1);
                Date m = c.getTime();
                String endYear = format.format(m);
                fundNetValueInfoReq.setBeginDate(fundNetValueInfoReq.getBeginDate());
                fundNetValueInfoReq.setEndDate(endYear);
            }
            //新增innerFundCode
            if (StringUtils.isNotBlank(fundNetValueInfoReq.getInnerFundCode())){
                queryWrapper.eq("inner_fund_code",fundNetValueInfoReq.getInnerFundCode());
            }
            try {
                LocalDate beginDate = LocalDate.parse(fundNetValueInfoReq.getBeginDate(), DateTimeFormatter.ofPattern("yyyyMMdd"));
                LocalDate endDate = LocalDate.parse(fundNetValueInfoReq.getEndDate(), DateTimeFormatter.ofPattern("yyyyMMdd"));
                Duration duration = Duration.between(beginDate.atTime(0, 0, 0),endDate.atTime(0, 0, 0));
                long days = duration.toDays(); //相差的天数
                if (days < 0) {
                    return R.error(RetCodeEnum.PARAM_ERROR.getCode(),"endDate早于beginDate");
                }
                if (days > 366) {
                    return R.error(RetCodeEnum.PARAM_ERROR.getCode(),"beginDate与endDate间隔的天数不能超过365天");
                }
            } catch (Exception e) {
                log.error("参数有误：beginDate :{"+ fundNetValueInfoReq.getBeginDate() + "}, endDate :{"+ fundNetValueInfoReq.getEndDate() +"}",e);
                return R.error(RetCodeEnum.PARAM_ERROR.getCode(),"beginDate或endDate格式不正确");
            }
            queryWrapper.ge("net_value_date", fundNetValueInfoReq.getBeginDate());
            queryWrapper.le("net_value_date",fundNetValueInfoReq.getEndDate());
            queryWrapper.orderBy(true,false, "net_value_date","fund_code","inner_fund_code","trust_no");

            //2 执行检索
            IPage<FundNetValueInfoEntity> page = this.page(
                    new Query<FundNetValueInfoEntity>().getPage(pageParams),
                    queryWrapper
            );
            //封装数据
            PageUtils pageUtils = new PageUtils(page);
            List<FundNetValueInfoEntity> entities = page.getRecords();
            //entities 转vo
            ArrayList<FundNetValueInfoVo> vos = new ArrayList<>();
            for (FundNetValueInfoEntity entity : entities) {
                vos.add(FundNetValueInfoVo.builder()
                        .id(entity.getId())
                        .fundCode(StringUtils.isBlank(entity.getFundCode())?"":entity.getFundCode())
                        .innerFundCode(entity.getInnerFundCode())
                        .fundName(entity.getFundName())
                        .fundLevel(StringUtils.isBlank(entity.getFundLevel())?"":entity.getFundLevel())
                        .netValueDate(Objects.isNull(entity.getNetValueDate())?null:entity.getNetValueDate().split(" ")[0].replace("-",""))
                        .netValue(Objects.isNull(entity.getNetValue())?"":entity.getNetValue().toString())
                        .totalNetValue(Objects.isNull(entity.getTotalNetValue())?"":entity.getTotalNetValue().toString())
                        .dividendYield(Objects.isNull(entity.getDividendYield())?"":entity.getDividendYield().toString())
                        .shares(Objects.isNull(entity.getShares())?"":entity.getShares().toString())
                        .assetValue(Objects.isNull(entity.getAssetValue())?"":entity.getAssetValue().toString())
                        .totalAsset(Objects.isNull(entity.getTotalAsset())?"":entity.getTotalAsset().toString())
                        .ettFundIncome(Objects.isNull(entity.getEttFundIncome())?"":entity.getEttFundIncome().toString())
                        .sevenDayRate(Objects.isNull(entity.getSevenDayRate())?"":entity.getSevenDayRate().toString())
                        .currency(entity.getCurrency())
                        .accumulated(Objects.isNull(entity.getAccumulated())?"":entity.getAccumulated().toString())
                        .checkStatus(entity.getCheckStatus())
                        .trustNo(entity.getTrustNo())
                        .build());
            }
            pageUtils.setList(vos);
            log.info("基金净值信息查询成功...");
            return R.ok("基金净值信息查询成功").put("data",pageUtils);
        } catch (RRException rrException) {
            log.error("the QueryDataVo is :" + JSON.toJSONString(vo) + ".the FundNetValueInfoServiceImpl RRException is :", rrException);
            return R.error(RetCodeEnum.OPERATION_ERROR.getCode(), rrException.getMsg());
        } catch (Exception e) {
            log.error("the QueryDataVo is :" + JSON.toJSONString(vo) + ".the FundNetValueInfoServiceImpl Exception is :", e);
            return R.error(RetCodeEnum.OPERATION_ERROR.getCode(), "查询数据失败!");
        }
    }

    @Override
    public R updateData(String type, List<FundNetValueInfoEntity> datas) {
        return null;
    }



    @Override
    public BigDecimal getSharesByUniqueKey(FundNetValueInfoEntity req) {
        try {
            FundNetValueInfoEntity entity = baseMapper.selectByUniqueKey(req);
            if (!Objects.isNull(entity))
                return entity.getShares();
        } catch (Exception e) {
            log.error("getSharesByUniqueKey::", e);
        }
        return null;
    }

    @Override
    public BigDecimal getSharesByFundInfo(FundNetValueInfoEntity req) {
        try {
            FundNetValueInfoEntity entity = baseMapper.getSharesByFundInfo(req);
            if (!Objects.isNull(entity))
                return entity.getShares();
        } catch (Exception e) {
            log.error("getSharesByUniqueKey::", e);
        }
        return null;
    }

   public  FundNetValueInfoEntity getNewestByInnerFundCodeAndFundName(String innerFundCode,String fundName,String trustNo){
       QueryWrapper<FundNetValueInfoEntity> queryWrapper = new QueryWrapper<>();
       queryWrapper.eq("inner_fund_code",innerFundCode );
       queryWrapper.eq("fund_name",fundName);
       queryWrapper.eq("trust_no",trustNo);
       queryWrapper.orderByDesc("net_value_date");
       queryWrapper.last("limit 1");

       FundNetValueInfoEntity fundNetValueInfoEntity = baseMapper.selectOne(queryWrapper);
       log.info("getNewestByInnerFundCodeAndFundName_innerFundCode={},fundName={},trustNo={},result={}",innerFundCode,fundName,trustNo,JSON.toJSONString(fundNetValueInfoEntity));
       return fundNetValueInfoEntity;
   }

    public FundNetValueInfoEntity getItemByFundCodeAndNetValueDate(String fundCode,String netValueDate,String trustNo){
        QueryWrapper<FundNetValueInfoEntity> queryWrapper = new QueryWrapper<FundNetValueInfoEntity>()
                .eq("fund_code", fundCode)
                .eq("net_value_date",netValueDate)
                .eq("trust_no",trustNo)
                .last("limit 1");

        FundNetValueInfoEntity fundNetValueInfoEntity = baseMapper.selectOne(queryWrapper);
        log.info("getItemByFundCodeAndNetValueDate_fundCode={},netValueDate={},trustNo={},result={}",fundCode,netValueDate,trustNo,JSON.toJSONString(fundNetValueInfoEntity));
        return fundNetValueInfoEntity;
    }

    public String getFundCodeByInnerFundCode(String fundCode) {
        return fundNetValueInfoDao.getFundCodeByInnerFundCode(fundCode);
    }

    public List<FundNetValueInfoEntity> selectByTenant(TenantEscrowAuthorizationInfoDto tenant) {
        QueryWrapper<FundNetValueInfoEntity> queryWrapper = new QueryWrapper<FundNetValueInfoEntity>()
                .eq("admin", tenant.getTenantName())
                .eq("trust_no", tenant.getTrustNo());
        List<FundNetValueInfoEntity> fundNetValueInfoEntities = baseMapper.selectList(queryWrapper);
        return fundNetValueInfoEntities;
    }

    public List<FundNetValueInfoEntity> getFundNameByCode(String admin, String fundCode, String trustNo) {
        if (StringUtils.isNotEmpty(admin) && StringUtils.isNotEmpty(fundCode) && StringUtils.isNotEmpty(trustNo)) {
            QueryWrapper<FundNetValueInfoEntity> queryWrapper = new QueryWrapper<FundNetValueInfoEntity>()
                    .eq("admin", admin)
                    .eq("fund_code", fundCode)
                    .eq("trust_no", trustNo);
            return baseMapper.selectList(queryWrapper);
        }
        return new ArrayList<>();
    }

    public int updateTotalAsset(FundNetValueInfoEntity entity) {
        return baseMapper.updateTotalAssetData(entity);
    }

    private String getFundCodeByFundName(String fundName, String trustNo, String tenantName) {
        //1先从数据库查，应该可能已经落过一些数据了
        String fundCode = fundInfoService.getFundCodeByFundName(fundName, trustNo, tenantName);
        if(StringUtils.isNotBlank(fundCode)){
            return fundCode;
        }
        //查询字典表
        return zxFndBasicInfoCyService.getFundCodeByFundname(fundName);
    }
}