package com.xy.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yitter.idgen.YitIdHelper;
import com.xy.collections.list.JArrayList;
import com.xy.collections.list.JList;
import com.xy.config.DeviceThreadPoolConfig;
import com.xy.config.FileConfig;
import com.xy.device.EnumSimConfig;
import com.xy.dto.DeviceAlgorithmChargingHistoryDto;
import com.xy.dto.DeviceSimChargeDto;
import com.xy.dto.MercFeeConfigDto;
import com.xy.dto.OrderMercManageDto;
import com.xy.dto.be.MercDto;
import com.xy.entity.DeviceSim;
import com.xy.entity.DeviceSimCharge;
import com.xy.entity.SysDictRedis;
import com.xy.mapper.DeviceSimChargeMapper;
import com.xy.service.be.MercService;
import com.xy.util.ExcelUtils;
import com.xy.utils.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.experimental.Accessors;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.xy.utils.Beans.copy;
import static com.xy.utils.PlusBeans.toIPage;
import static com.xy.utils.PlusBeans.toPageBean;


/**
 * <p>
 * 设备流量卡充值表 服务实现类
 * </p>
 *
 * @author lijin
 * @since 2023-10-16
 */
@Service
@AllArgsConstructor
@Api(tags = "设备流量卡充值表")
public class DeviceSimChargeServiceImpl extends ServiceImpl<DeviceSimChargeMapper, DeviceSimCharge> implements DeviceSimChargeService {

    private OrderMercManageService orderMercManageService;

    private MercService mercService;

    private DeviceChargingHistoryServiceImpl deviceChargingHistoryService;

    private HttpServletResponse response;

    private FileConfig fileConfig;

    private MercFeeConfigService mercFeeConfigService;

    private DeviceSimServiceImpl deviceSimService;


    @PostMapping("page")
    @ApiOperation("分页查询")
    public R<PageBean<DeviceSimChargeDto.Vo>> page(@RequestBody DeviceSimChargeDto.Page page) {
        PageBean pageBean = page.getPage();
        LambdaQueryWrapper<DeviceSimCharge> lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(page, DeviceSimCharge.class)
                .ge(DeviceSimCharge::getCreateTime, page.getBeginCreateTime())
                .le(DeviceSimCharge::getCreateTime, page.getEndCreateTime())
                .build()
                .orderByDesc(!Emptys.check(pageBean.getOrders()), DeviceSimCharge::getCreateTime);
        IPage<DeviceSimCharge> iPage = page(toIPage(pageBean), lambdaQueryWrapper);
        PageBean<DeviceSimChargeDto.Vo> voPageBean = toPageBean(DeviceSimChargeDto.Vo.class, iPage);
        List<DeviceSimChargeDto.Vo> records = voPageBean.getRecords();
        if (Emptys.check(records)) {
            copy(records)
                    .target(() -> orderMercManageService.list(new OrderMercManageDto.SelectList().setId(new JArrayList<>(records).getProperty(DeviceSimChargeDto.Vo::getOrderId))).getData()
                            , DeviceSimChargeDto.Vo::getOrderId, DeviceSimChargeDto.Vo::getFiles, OrderMercManageDto.Vo::getId, OrderMercManageDto.Vo::getFiles
                    ).target(() -> mercService.list(new MercDto.SelectList().setMercIds(new JArrayList<>(records).getProperty(DeviceSimChargeDto.Vo::getMercId).comparing())).getData()
                            , DeviceSimChargeDto.Vo::getMercId, DeviceSimChargeDto.Vo::getMercName, MercDto.Vo::getId, MercDto.Vo::getName)
                    .builder();
        }
        return R.ok(voPageBean);
    }

    public R<PageBean<DeviceSimChargeDto.PageByTopMercVO>> pageByTopMerc(DeviceSimChargeDto.PageByTopMerc page) {
        Long curMercId = page.getCurMercId();
        Long chooseMercId = page.getChooseMercId();
        List<Long> mercIds = new ArrayList<>();
        if (chooseMercId == null) {
            mercIds = R.feignCheckData(mercService.getChildMercIds(new MercDto.QuerySubDTO().setParentMercID(curMercId)));
        } else {
            // 指定商户ID
            mercIds.add(chooseMercId);
        }
        if (CollUtil.isEmpty(mercIds)) {
            return R.ok();
        }
        PageBean pageBean = page.getPage();
        LambdaQueryWrapper<DeviceSimCharge> lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(page, DeviceSimCharge.class)
                .ge(DeviceSimCharge::getCreateTime, page.getBeginCreateTime())
                .le(DeviceSimCharge::getCreateTime, page.getEndCreateTime())
                .in(DeviceSimCharge::getMercId, mercIds)
                .build()
                .orderByDesc(!Emptys.check(pageBean.getOrders()), DeviceSimCharge::getCreateTime);
        IPage<DeviceSimCharge> iPage = page(toIPage(pageBean), lambdaQueryWrapper);
        PageBean<DeviceSimChargeDto.PageByTopMercVO> voPageBean = toPageBean(DeviceSimChargeDto.PageByTopMercVO.class, iPage);
        List<DeviceSimChargeDto.PageByTopMercVO> records = voPageBean.getRecords();

        if (Emptys.check(records)) {
            // 佣金配置查询
            List<MercFeeConfigDto.ListFeeConfigByMercVO> feeConfigByMercVOS = R.feignCheckData(mercFeeConfigService.listFeeConfigByMerc(new MercFeeConfigDto.ListFeeConfigByMercDTO().setMercId(curMercId)));
            Map<Long, MercFeeConfigDto.ListFeeConfigByMercVO> configByMercVOMap = MapUtil.newHashMap();
            if (CollUtil.isNotEmpty(feeConfigByMercVOS)) {
                configByMercVOMap = feeConfigByMercVOS.stream().collect(Collectors.toMap(MercFeeConfigDto.ListFeeConfigByMercVO::getMercId, f -> f));
            }
            copy(records)
                    .target(() -> orderMercManageService.list(new OrderMercManageDto.SelectList().setId(new JArrayList<>(records).getProperty(DeviceSimChargeDto.PageByTopMercVO::getOrderId))).getData()
                            , DeviceSimChargeDto.PageByTopMercVO::getOrderId, DeviceSimChargeDto.PageByTopMercVO::getFiles, OrderMercManageDto.Vo::getId, OrderMercManageDto.Vo::getFiles
                    ).target(() -> mercService.list(new MercDto.SelectList().setMercIds(new JArrayList<>(records).getProperty(DeviceSimChargeDto.PageByTopMercVO::getMercId).comparing())).getData()
                            , DeviceSimChargeDto.PageByTopMercVO::getMercId, DeviceSimChargeDto.PageByTopMercVO::getMercName, MercDto.Vo::getId, MercDto.Vo::getName)
                    .builder();
            List<String> simIds = records.stream().map(DeviceSimChargeDto.PageByTopMercVO::getSimId).collect(Collectors.toList());
            List<DeviceSim> deviceSims = deviceSimService.listByIds(simIds);
            Map<String, DeviceSim> deviceSimMap = MapUtil.newHashMap();
            if (CollUtil.isNotEmpty(deviceSims)) {
                deviceSimMap = deviceSims.stream().collect(Collectors.toMap(DeviceSim::getId, ds -> ds));
            }
            Map<String, SysDictRedis> simConfig = SysDictUtils.get(EnumSimConfig.Code.CODE.getCode());

            Integer value = Integer.valueOf(simConfig.get(EnumSimConfig.N_200.getCode()).getValue());

            for (DeviceSimChargeDto.PageByTopMercVO record : records) {
                Long mercId = record.getMercId();
                String simId = record.getSimId();
                // 佣金初始化
                Integer extraMoney = 0;
                MercFeeConfigDto.ListFeeConfigByMercVO listFeeConfigByMercVO = configByMercVOMap.get(mercId);
                if (listFeeConfigByMercVO != null) {
                    extraMoney = listFeeConfigByMercVO.getFeeConfig().getFeeSimcard();
                    record.setAgentMoney(extraMoney);
                }
                DeviceSim sim = deviceSimMap.get(simId);
                // 封装过期状态说明
                if (Emptys.check(sim)) {
                    LocalDateTime timeout = sim.getTimeout();
                    if (timeout == null) {
                        record.setTimeoutStatus("未初始化");
                    } else {
                        String timeoutStatus;
                        long s = DataTime.diff(LocalDateTime.now(), timeout, "s");
                        if (s <= 0) {
                            timeoutStatus = "欠费(" + (~(s / 86400 - 1)) + "天)";
                        } else {
                            timeoutStatus = s <= value * 86400 ? "即将到期(" + s / 86400 + "天)" : "正常(" + s / 86400 + "天)";
                        }
                        record.setTimeoutStatus(timeoutStatus);
                    }
                    record.setTimeout(timeout);
                }

            }
        }
        return R.ok(voPageBean);

    }

    /**
     * 财务管理-支出管理-流量卡费明细
     *
     * @param page
     * @return
     */

    public R<PageBean<DeviceSimChargeDto.MySpendPageVO>> mySpendPage(DeviceSimChargeDto.PageByTopMerc page) {
        Long curMercId = page.getCurMercId();
        List<Long> mercIds = new ArrayList<>();
        // 指定商户ID
        mercIds.add(curMercId);
        PageBean pageBean = page.getPage();
        LambdaQueryWrapper<DeviceSimCharge> lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(page, DeviceSimCharge.class)
                .ge(DeviceSimCharge::getCreateTime, page.getBeginCreateTime())
                .le(DeviceSimCharge::getCreateTime, page.getEndCreateTime())
                .in(DeviceSimCharge::getMercId, mercIds)
                .build()
                .orderByDesc(!Emptys.check(pageBean.getOrders()), DeviceSimCharge::getCreateTime);
        IPage<DeviceSimCharge> iPage = page(toIPage(pageBean), lambdaQueryWrapper);
        PageBean<DeviceSimChargeDto.MySpendPageVO> voPageBean = toPageBean(DeviceSimChargeDto.MySpendPageVO.class, iPage);
        List<DeviceSimChargeDto.MySpendPageVO> records = voPageBean.getRecords();

        if (Emptys.check(records)) {
            List<OrderMercManageDto.Vo> orderMercManages = orderMercManageService.list(new OrderMercManageDto.SelectList().setId(new JArrayList<>(records).getProperty(DeviceSimChargeDto.MySpendPageVO::getOrderId))).getData();
            copy(records)
                    .target(() -> orderMercManages, DeviceSimChargeDto.MySpendPageVO::getOrderId, DeviceSimChargeDto.MySpendPageVO::getFiles, OrderMercManageDto.Vo::getId, OrderMercManageDto.Vo::getFiles)
                    .target(() -> orderMercManages, DeviceSimChargeDto.MySpendPageVO::getOrderId, DeviceSimChargeDto.MySpendPageVO::getBalanceMone, OrderMercManageDto.Vo::getId, OrderMercManageDto.Vo::getBalanceMoney)
                    .target(() -> mercService.list(new MercDto.SelectList().setMercIds(new JArrayList<>(records).getProperty(DeviceSimChargeDto.MySpendPageVO::getMercId).comparing())).getData()
                            , DeviceSimChargeDto.MySpendPageVO::getMercId, DeviceSimChargeDto.MySpendPageVO::getMercName, MercDto.Vo::getId, MercDto.Vo::getName)
                    .builder();
            List<String> simIds = records.stream().map(DeviceSimChargeDto.MySpendPageVO::getSimId).collect(Collectors.toList());
            List<DeviceSim> deviceSims = deviceSimService.listByIds(simIds);
            Map<String, DeviceSim> deviceSimMap = MapUtil.newHashMap();
            if (CollUtil.isNotEmpty(deviceSims)) {
                deviceSimMap = deviceSims.stream().collect(Collectors.toMap(DeviceSim::getId, ds -> ds));
            }
            Map<String, SysDictRedis> simConfig = SysDictUtils.get(EnumSimConfig.Code.CODE.getCode());

            Integer value = Integer.valueOf(simConfig.get(EnumSimConfig.N_200.getCode()).getValue());

            for (DeviceSimChargeDto.MySpendPageVO record : records) {
                Long mercId = record.getMercId();
                String simId = record.getSimId();
                DeviceSim sim = deviceSimMap.get(simId);
                // 封装过期状态说明
                if (Emptys.check(sim)) {
                    LocalDateTime timeout = sim.getTimeout();
                    if (timeout == null) {
                        record.setTimeoutStatus("未初始化");
                    } else {
                        String timeoutStatus;
                        long s = DataTime.diff(LocalDateTime.now(), timeout, "s");
                        if (s <= 0) {
                            timeoutStatus = "欠费(" + (~(s / 86400 - 1)) + "天)";
                        } else {
                            timeoutStatus = s <= value * 86400 ? "即将到期(" + s / 86400 + "天)" : "正常(" + s / 86400 + "天)";
                        }
                        record.setTimeoutStatus(timeoutStatus);
                    }
                    record.setTimeout(timeout);
                }

            }
        }
        return R.ok(voPageBean);

    }


    @ApiOperation("月统计分页")
    @PostMapping("moonCountPage")
    public R<PageBean<DeviceSimChargeDto.MoonCountVo>> moonCountPage(@RequestBody DeviceSimChargeDto.MoonCountPage moonCountPage) {
        JList<DeviceSimChargeDto.MoonCountVo> moonCountVos = new JArrayList<>();
        // 查询数据
        List<String> attrNames = Arrays.asList(
                LambdaUtils.getUnderlineCaseName(DeviceSimCharge::getMercId)
        );
        List<String> selectNames = Arrays.asList(
                attrNames.get(0),
                String.format("count(*) %s", LambdaUtils.getProperty(DeviceSimCharge::getSize)),
                String.format("sum(%s) %s", LambdaUtils.getUnderlineCaseName(DeviceSimCharge::getMoney), LambdaUtils.getProperty(DeviceSimCharge::getMoney))
        );
        QueryWrapper<DeviceSimCharge> queryWrapper = new QueryWrapper<DeviceSimCharge>()
                .select(selectNames.toArray(new String[selectNames.size()]))
                .in(Emptys.check(moonCountPage.getMercIds()), attrNames.get(0), moonCountPage.getMercIds())
                .between("DATE_FORMAT(" + LambdaUtils.getUnderlineCaseName(DeviceSimCharge::getCreateTime) + ", '%Y-%m-%d')", moonCountPage.getBeginDate(), moonCountPage.getEndDate())
                .groupBy(attrNames);
        IPage<DeviceSimCharge> iPage = page(toIPage(moonCountPage.getPage()), queryWrapper);
        List<DeviceSimCharge> records = iPage.getRecords();
        if (!Emptys.check(records)) {
            return R.ok(new PageBean<>());
        }
        String date = String.format("%s~%s", moonCountPage.getBeginDate(), moonCountPage.getEndDate());
        records.forEach(deviceSimCharge -> {
            DeviceSimChargeDto.MoonCountVo moonCountVo = new DeviceSimChargeDto.MoonCountVo()
                    .setDate(date)
                    .setMercId(deviceSimCharge.getMercId())
                    .setSimSize(deviceSimCharge.getSize())
                    .setMoney(deviceSimCharge.getMoney());
            moonCountVos.add(moonCountVo);
        });
        // 翻译商户名称
        Beans.copy(moonCountVos).target(() -> mercService.list(new MercDto.SelectList().setMercIds(moonCountVos.getProperty(DeviceSimChargeDto.MoonCountVo::getMercId).comparing())).getData(),
                        DeviceSimChargeDto.MoonCountVo::getMercId, DeviceSimChargeDto.MoonCountVo::getMercName, MercDto.Vo::getId, MercDto.Vo::getName)
                .builder();
        PageBean<DeviceSimChargeDto.MoonCountVo> pageBean = new PageBean<DeviceSimChargeDto.MoonCountVo>()
                .setCurrent(iPage.getCurrent())
                .setSize(iPage.getSize())
                .setTotal(iPage.getTotal())
                .setRecords(moonCountVos);
        return R.ok(pageBean);
    }

    @ApiOperation("月统计")
    @PostMapping("moonCount")
    public R<List<DeviceSimChargeDto.MoonCountVo>> moonCount(@RequestBody DeviceSimChargeDto.MoonCount moonCount) {
        JList<DeviceSimChargeDto.MoonCountVo> moonCountVos = new JArrayList<>();
        // 查询数据
        List<String> attrNames = Arrays.asList(
                LambdaUtils.getUnderlineCaseName(DeviceSimCharge::getMercId),
                "DATE_FORMAT(" + LambdaUtils.getUnderlineCaseName(DeviceSimCharge::getCreateTime) + ", '%Y-%m')"
        );
        List<String> selectNames = Arrays.asList(
                attrNames.get(0),
                attrNames.get(1) + " " + LambdaUtils.getProperty(DeviceSimCharge::getNote),
                String.format("count(*) %s", LambdaUtils.getProperty(DeviceSimCharge::getSize)),
                String.format("sum(%s) %s", LambdaUtils.getUnderlineCaseName(DeviceSimCharge::getMoney), LambdaUtils.getProperty(DeviceSimCharge::getMoney))
        );
        List<String> dateDifference = deviceChargingHistoryService.getDateDifference(moonCount.getBeginDate(), moonCount.getEndDate());
        ThreadPoolUtils.Execute execute = ThreadPoolUtils.excPoll(DeviceThreadPoolConfig.DEVICE_COMMON_POLL, dateDifference.size());
        dateDifference.forEach(date -> execute.execute(() -> {
            QueryWrapper<DeviceSimCharge> queryWrapper = new QueryWrapper<DeviceSimCharge>()
                    .select(selectNames.get(0), selectNames.get(1), selectNames.get(2), selectNames.get(3))
                    .in(Emptys.check(moonCount.getMercIds()), attrNames.get(0), moonCount.getMercIds())
                    .eq(attrNames.get(1), date)
                    .groupBy(attrNames.get(0), attrNames.get(1));
            List<DeviceSimCharge> deviceSimCharges = list(queryWrapper);
            deviceSimCharges.forEach(deviceSimCharge -> {
                DeviceSimChargeDto.MoonCountVo moonCountVo = new DeviceSimChargeDto.MoonCountVo()
                        .setDate(deviceSimCharge.getNote())
                        .setMercId(deviceSimCharge.getMercId())
                        .setSimSize(deviceSimCharge.getSize())
                        .setMoney(deviceSimCharge.getMoney());
                moonCountVos.add(moonCountVo);
            });
        }));
        execute.end();
        // 翻译商户名称
        Beans.copy(moonCountVos).target(() -> mercService.list(new MercDto.SelectList().setMercIds(moonCountVos.getProperty(DeviceSimChargeDto.MoonCountVo::getMercId).comparing())).getData(),
                        DeviceSimChargeDto.MoonCountVo::getMercId, DeviceSimChargeDto.MoonCountVo::getMercName, MercDto.Vo::getId, MercDto.Vo::getName)
                .builder();
        return R.ok(moonCountVos.desc(DeviceSimChargeDto.MoonCountVo::getDate));
    }

    @SneakyThrows
    @ApiOperation("月统计导出")
    @PostMapping("moonCountDownload")
    public void moonCountDownload(@RequestBody DeviceSimChargeDto.MoonCount moonCount) {
        // 生成excel
        String name = YitIdHelper.nextId() + ".xlsx";
        String path = fileConfig.getPath() + File.separator + name;
        ExcelUtils.SheetAndData<MoonCountData> sheetAndData = ExcelUtils.create(path, MoonCountData.class);
        sheetAndData.sheet("设备流量卡月统计数据", () -> {
            List<DeviceSimChargeDto.MoonCountVo> data = moonCount(moonCount).getData();
            if (!Emptys.check(data)) {
                return null;
            }
            return Beans.copy(MoonCountData.class, data);
        }).builder();
        // 下载文件
        InputStream inputStream = IoUtils.inputStream(path).get();
        response.setHeader("Content-Disposition", "attachment; filename=" + "设备管理费月统计数据.xlsx");
        response.setContentType("application/xlsx");
        byte[] buffer = new byte[1024];
        int bytesRead;
        OutputStream outputStream = response.getOutputStream();
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        inputStream.close();
        outputStream.close();
        // 删除文件
        new File(path).delete();
    }

    @Data
    @Accessors(chain = true)
    public static class MoonCountData {

        @ColumnWidth(25)
        @ExcelProperty(value = "统计时间 yyyy-MM")
        private String date;

        @ColumnWidth(25)
        @ExcelProperty(value = "商户id")
        private Long mercId;

        @ColumnWidth(25)
        @ExcelProperty(value = "商户名称")
        private String mercName;

        @ColumnWidth(25)
        @ExcelProperty(value = "卡数")
        private Integer simSize;

        @ColumnWidth(25)
        @ExcelProperty(value = "续费金额")
        private Integer money;
    }
}
