package com.usky.fire.service.impl;

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.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.usky.common.core.bean.CommonPage;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.core.util.Arith;
import com.usky.common.core.util.DateUtils;
import com.usky.common.mybatis.core.AbstractCrudService;
import com.usky.fire.domain.DemPoliceInfo;
import com.usky.fire.domain.DemPoliceRecord;
import com.usky.fire.mapper.DemPoliceInfoMapper;
import com.usky.fire.service.*;
import com.usky.fire.service.vo.AlertStatisticsVO;
import com.usky.fire.service.vo.DemPoliceRecordExportVO;
import com.usky.fire.service.vo.FireLevelRatioVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * 警情信息 服务实现类
 * </p>
 *
 * @author JCB
 * @since 2022-10-24
 */
@Service
public class DemPoliceInfoServiceImpl extends AbstractCrudService<DemPoliceInfoMapper, DemPoliceInfo> implements DemPoliceInfoService {

    public static final String[] ALERT_TYPE = {"火灾", "社会救助", "抢险救援", "排爆安检"};

    @Autowired
    private DemFireStatisticsAttachService demFireStatisticsAttachService;

    @Autowired
    private DemPoliceRecordService demPoliceRecordService;

    @Override
    public CommonPage<Object> policeRecordList(String caseCode, String branch, Integer id, Integer pageNum, Integer pageSize) {
        List<Object> list = new ArrayList<>();
        IPage<DemPoliceInfo> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<DemPoliceInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(DemPoliceInfo::getId, DemPoliceInfo::getCaseCode, DemPoliceInfo::getBranch,
                DemPoliceInfo::getTimeSlot, DemPoliceInfo::getArea, DemPoliceInfo::getCaseType,
                DemPoliceInfo::getFilingDate, DemPoliceInfo::getFilingTime, DemPoliceInfo::getHandleObject,
                DemPoliceInfo::getCaseStatus, DemPoliceInfo::getCaseNature, DemPoliceInfo::getDispatchVehicle,
                DemPoliceInfo::getPoliceId)
                .like(StringUtils.isNotBlank(branch), DemPoliceInfo::getBranch, branch)
                .like(StringUtils.isNotBlank(caseCode), DemPoliceInfo::getCaseCode, caseCode)
                .orderByDesc(DemPoliceInfo::getId);
        page = this.page(page, queryWrapper);
        if (CollectionUtils.isNotEmpty(page.getRecords())) {
            List<String> policeIdList = new ArrayList<>();
            for (int i = 0; i < page.getRecords().size(); i++) {
                policeIdList.add(page.getRecords().get(i).getPoliceId());
            }
            if (CollectionUtils.isNotEmpty(policeIdList)) {
                LambdaQueryWrapper<DemPoliceRecord> queryWrapper1 = Wrappers.lambdaQuery();
                queryWrapper1.eq(DemPoliceRecord::getDeleteFlag, "0")
                        .in(DemPoliceRecord::getCaseInfoId, policeIdList);
                List<DemPoliceRecord> list1 = demPoliceRecordService.list(queryWrapper1);
                for (int i = 0; i < page.getRecords().size(); i++) {
                    for (int j = 0; j < list1.size(); j++) {
                        if (page.getRecords().get(i).getPoliceId().equals(list1.get(j).getCaseInfoId())) {
                            Map<String, Object> map = new HashMap<>();
                            map.put("id", page.getRecords().get(i).getId());
                            map.put("handleStartTime", list1.get(j).getHandleStartTime());
                            map.put("handleEndTime", list1.get(j).getHandleEndTime());
                            map.put("id1", list1.get(j).getId());
                            map.put("caseCode", page.getRecords().get(i).getCaseCode());
                            map.put("branch", page.getRecords().get(i).getBranch());
                            map.put("timeSlot", page.getRecords().get(i).getTimeSlot());
                            map.put("area", page.getRecords().get(i).getArea());
                            map.put("caseType", page.getRecords().get(i).getCaseType());
                            map.put("filingDate", page.getRecords().get(i).getFilingDate());
                            map.put("filingTime", page.getRecords().get(i).getFilingTime());
                            map.put("handleObject", page.getRecords().get(i).getHandleObject());
                            map.put("caseStatus", page.getRecords().get(i).getCaseStatus());
                            map.put("caseNature", page.getRecords().get(i).getCaseNature());
                            map.put("dispatchVehicle", page.getRecords().get(i).getDispatchVehicle());
                            list.add(map);
                        }
                    }
                }
            }
        }
        return new CommonPage<>(list, page.getTotal(), pageSize, pageNum);
    }

    @Override
    public List<DemPoliceRecordExportVO> policeRecordListExport(String caseCode, String branch){
        List<DemPoliceRecordExportVO> list = new ArrayList<>();
        LambdaQueryWrapper<DemPoliceInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(DemPoliceInfo::getId, DemPoliceInfo::getCaseCode, DemPoliceInfo::getBranch,
                DemPoliceInfo::getTimeSlot, DemPoliceInfo::getArea, DemPoliceInfo::getCaseType,
                DemPoliceInfo::getFilingDate, DemPoliceInfo::getFilingTime, DemPoliceInfo::getHandleObject,
                DemPoliceInfo::getCaseStatus, DemPoliceInfo::getCaseNature, DemPoliceInfo::getDispatchVehicle,
                DemPoliceInfo::getPoliceId)
                .like(StringUtils.isNotBlank(branch), DemPoliceInfo::getBranch, branch)
                .like(StringUtils.isNotBlank(caseCode), DemPoliceInfo::getCaseCode, caseCode)
                .orderByDesc(DemPoliceInfo::getId).last("limit 1000");
        List<DemPoliceInfo> list1 = this.list(queryWrapper);
        if (CollectionUtils.isNotEmpty(list1)) {
            List<String> policeIdList = new ArrayList<>();
            for (int i = 0; i < list1.size(); i++) {
                policeIdList.add(list1.get(i).getPoliceId());
            }
            if (CollectionUtils.isNotEmpty(policeIdList)) {
                LambdaQueryWrapper<DemPoliceRecord> queryWrapper1 = Wrappers.lambdaQuery();
                queryWrapper1.eq(DemPoliceRecord::getDeleteFlag, "0")
                        .in(DemPoliceRecord::getCaseInfoId, policeIdList);
                List<DemPoliceRecord> list2 = demPoliceRecordService.list(queryWrapper1);
                for (int i = 0; i < list1.size(); i++) {
                    for (int j = 0; j < list2.size(); j++) {
                        if (list1.get(i).getPoliceId().equals(list2.get(j).getCaseInfoId())) {
                            DemPoliceRecordExportVO demPoliceRecordExportVO = new DemPoliceRecordExportVO();
                            demPoliceRecordExportVO.setCaseCode(list1.get(i).getCaseCode());
                            demPoliceRecordExportVO.setHandleStartTime(list2.get(j).getHandleStartTime());
                            demPoliceRecordExportVO.setHandleEndTime(list2.get(j).getHandleEndTime());
                            demPoliceRecordExportVO.setBranch(list1.get(i).getBranch());
                            demPoliceRecordExportVO.setTimeSlot(list1.get(i).getTimeSlot());
                            demPoliceRecordExportVO.setArea(list1.get(i).getArea());
                            demPoliceRecordExportVO.setCaseType(list1.get(i).getCaseType());
                            demPoliceRecordExportVO.setFilingDate(list1.get(i).getFilingDate());
                            demPoliceRecordExportVO.setFilingTime(list1.get(i).getFilingTime());
                            demPoliceRecordExportVO.setHandleObject(list1.get(i).getHandleObject());
                            demPoliceRecordExportVO.setCaseStatus(list1.get(i).getCaseStatus());
                            demPoliceRecordExportVO.setCaseNature(list1.get(i).getCaseNature());
                            demPoliceRecordExportVO.setDispatchVehicle(list1.get(i).getDispatchVehicle());
                            list.add(demPoliceRecordExportVO);
                        }
                    }
                }
            }
        }

        return list;
    }

    @Transactional
    @Override
    public void updatePoliceRecord(DemPoliceInfo demPoliceInfo) {
        this.updateById(demPoliceInfo);
        DemPoliceRecord demPoliceRecord = new DemPoliceRecord();
        demPoliceRecord.setId(demPoliceInfo.getId1());
        demPoliceRecord.setCaseCode(demPoliceInfo.getCaseCode());
        demPoliceRecord.setBranch(demPoliceInfo.getBranch());
        demPoliceRecord.setTimeSlot(demPoliceInfo.getTimeSlot());
        demPoliceRecord.setArea(demPoliceInfo.getArea());
        demPoliceRecord.setCaseType(demPoliceInfo.getCaseType());
        demPoliceRecord.setFilingDate(demPoliceInfo.getFilingDate());
        demPoliceRecord.setFilingTime(demPoliceInfo.getFilingTime());
        demPoliceRecord.setHandleObject(demPoliceInfo.getHandleObject());
        demPoliceRecord.setCaseStatus(demPoliceInfo.getCaseStatus());
        demPoliceRecord.setCaseNature(demPoliceInfo.getCaseNature());
        demPoliceRecord.setDispatchVehicle(demPoliceInfo.getDispatchVehicle());
        demPoliceRecordService.updateById(demPoliceRecord);
    }

    @Override
    public Map<String, List<AlertStatisticsVO>> getAlertStatisticsByMonth(String startTime, String endTime, String streetTown) {
        QueryWrapper<DemPoliceInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("month(filing_time) monthTime", "COUNT(id) as aCount", "case_type as aType")
                .between("filing_time", startTime, endTime)
                .eq(StringUtils.isNotBlank(streetTown), "street_town", streetTown)
                .groupBy("monthTime", "aType");
        Map<String, List<AlertStatisticsVO>> typeMap = enhanceList(this.listMaps(queryWrapper));
        Date maxDate = getMaxDate();
        Date minDate = getMinDate();
        int distanceOfTwoDateYear = Integer.parseInt(DateUtils.getYear(maxDate)) - Integer.parseInt(DateUtils.getYear(minDate)) + 1;
        perfect(typeMap, 13, distanceOfTwoDateYear, 1);
        return typeMap;
    }

    @Override
    public List<AlertStatisticsVO> getAlertStatistics(String startTime, String endTime, String streetTown) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date startTime1 = null;
        Date endTime1 = null;
        if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)) {
            try {
                startTime1 = simpleDateFormat.parse(startTime);
                endTime1 = simpleDateFormat.parse(endTime);
            } catch (Exception e) {
                throw new BusinessException("时间格式错误");
            }
        }
        //获取同比
        Date upStartTime = DateUtils.addYears(startTime1, -1);
        Date upEndTime = DateUtils.addYears(endTime1, -1);
        List<AlertStatisticsVO> list = new ArrayList<>();
        list.add(getAsV(startTime1, endTime1, ALERT_TYPE[0], streetTown));
        list.add(getAsV(startTime1, endTime1, ALERT_TYPE[1], streetTown));
        list.add(getAsV(startTime1, endTime1, ALERT_TYPE[2], streetTown));
        list.add(getAsV(startTime1, endTime1, ALERT_TYPE[3], streetTown));
        list.forEach(alertStatisticsVO -> {
            AlertStatisticsVO upAsv = getAsV(upStartTime, upEndTime, alertStatisticsVO.getType(), streetTown);
            Integer number = alertStatisticsVO.getNumber();
            Integer upNumber = upAsv.getNumber();
            if (number.equals(upNumber)) {
                alertStatisticsVO.setSameStatus(0);
            } else if (number > upNumber) {
                alertStatisticsVO.setSameStatus(1);
            } else {
                alertStatisticsVO.setSameStatus(2);
            }
            if (0 != upNumber) {
                double radio = Arith.div(Arith.sub(number, upNumber), upNumber);
                alertStatisticsVO.setRadio(Math.abs(radio));
            } else {
                if (number != 0) {
                    alertStatisticsVO.setRadio(1.00);
                } else {
                    alertStatisticsVO.setRadio(0.00);
                }
            }
        });
        return list;
    }

    @Override
    public CommonPage<DemPoliceInfo> page(Integer current, Integer size, String startTime, String endTime, String streetTown, String caseCode) {
        IPage<DemPoliceInfo> page = new Page<>(current, size);
        LambdaQueryWrapper<DemPoliceInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.between(StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime), DemPoliceInfo::getCreateTime, startTime, endTime)
                .eq(StringUtils.isNotBlank(streetTown), DemPoliceInfo::getStreetTown, streetTown)
                .eq(StringUtils.isNotBlank(caseCode), DemPoliceInfo::getCaseCode, caseCode)
                .orderByDesc(DemPoliceInfo::getCreateTime);
        page = this.page(page, queryWrapper);
        return this.ToCommonPage(page);
    }

    @Override
    public Map<String, List<AlertStatisticsVO>> getAlertStatisticsByHouse(String startTime, String endTime, String streetTown) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date startTime1 = null;
        Date endTime1 = null;
        try {
            startTime1 = simpleDateFormat.parse(startTime);
            endTime1 = simpleDateFormat.parse(endTime);
        } catch (Exception e) {
            throw new BusinessException("时间格式错误");
        }
        QueryWrapper<DemPoliceInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("HOUR(filing_time) monthTime", "COUNT(id) as aCount", "case_type as aType")
                .between("filing_time", startTime1, endTime1)
                .eq("street_town", streetTown)
                .groupBy("monthTime", "case_type");
        Map<String, List<AlertStatisticsVO>> typeMap = enhanceList(this.listMaps(queryWrapper));
        Date maxDate = getMaxDate();
        Date minDate = getMinDate();
        AtomicInteger distanceOfTwoDate = new AtomicInteger(1);
        Optional.ofNullable(maxDate)
                .ifPresent(md -> distanceOfTwoDate.set(DateUtils.getDistanceOfTwoDateNew(minDate, maxDate)));
        perfect(typeMap, 24, distanceOfTwoDate.get(), 0);
        collectByThree(typeMap, distanceOfTwoDate.get());
        return typeMap;
    }

    @Override
    public List<Map<String, Object>> warningInstanceScatterer(String streetTown, Integer id) {
        IPage<DemPoliceInfo> page = new Page<>(1, 500);
        LambdaQueryWrapper<DemPoliceInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(DemPoliceInfo::getAddress, DemPoliceInfo::getGisX, DemPoliceInfo::getGisY,
                DemPoliceInfo::getId, DemPoliceInfo::getCaseType, DemPoliceInfo::getSquadron,
                DemPoliceInfo::getFilingTime, DemPoliceInfo::getSupplement, DemPoliceInfo::getPoliceId)
                .eq(StringUtils.isNotBlank(streetTown), DemPoliceInfo::getStreetTown, streetTown)
                .eq(null != id && id != 0, DemPoliceInfo::getId, id)
                .isNotNull(DemPoliceInfo::getQuenchTime)
                .orderByDesc(DemPoliceInfo::getId);
        page = this.page(page, queryWrapper);
        List<Map<String, Object>> list = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(page.getRecords())) {
            for (int i = 0; i < page.getRecords().size(); i++) {
                Map<String, Object> map = new HashMap<>();
                map.put("id", page.getRecords().get(i).getId());
                map.put("policeId", page.getRecords().get(i).getPoliceId());
                map.put("address", page.getRecords().get(i).getAddress());
                map.put("longitude", page.getRecords().get(i).getGisX());
                map.put("latitude", page.getRecords().get(i).getGisY());
                map.put("caseType", page.getRecords().get(i).getCaseType());
                map.put("squadron", page.getRecords().get(i).getSquadron());
                map.put("filingTime", page.getRecords().get(i).getFilingTime());
                map.put("AlarmTime", page.getRecords().get(i).getFilingTime());
                map.put("supplement", page.getRecords().get(i).getSupplement());
                list.add(map);
            }
        }
        return list;
    }

    public void collectByThree(Map<String, List<AlertStatisticsVO>> map, Integer distanceOfTwoDate) {
        for (String type : map.keySet()) {
            List<AlertStatisticsVO> list = map.get(type);
            List<List<AlertStatisticsVO>> partition = Lists.partition(list, 3);
            List<AlertStatisticsVO> thList = new ArrayList<>();
            for (List<AlertStatisticsVO> threeList : partition) {
                double sum = threeList.stream().mapToDouble(AlertStatisticsVO::getNumber).sum();
                AlertStatisticsVO alertStatisticsVO = new AlertStatisticsVO();
                alertStatisticsVO.setType(type);
                alertStatisticsVO.setMonth(String.valueOf(partition.indexOf(threeList)));
                alertStatisticsVO.setAvg(Arith.div(sum, distanceOfTwoDate));
                thList.add(alertStatisticsVO);
            }
            map.put(type, thList);
        }
    }

    public AlertStatisticsVO getAsV(Date startTime, Date endTime, String type, String streetTown) {
        AlertStatisticsVO alertStatisticsVo = new AlertStatisticsVO();
        LambdaQueryWrapper<DemPoliceInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(StringUtils.isNotBlank(type), DemPoliceInfo::getCaseType, type)
                .eq(StringUtils.isNotBlank(streetTown), DemPoliceInfo::getStreetTown, streetTown);
        if (null != startTime && null != endTime) {
            queryWrapper.between(DemPoliceInfo::getArrivalTime, startTime, endTime);
        }
        alertStatisticsVo.setType(type);
        alertStatisticsVo.setNumber(this.count(queryWrapper));
        return alertStatisticsVo;
    }


    public Map<String, List<AlertStatisticsVO>> enhanceList(List<Map<String, Object>> mapList) {
        List<AlertStatisticsVO> list = new ArrayList<>();
        for (Map<String, Object> map : mapList) {
            AlertStatisticsVO alertStatisticsVo = new AlertStatisticsVO();
            alertStatisticsVo.setType(map.get("aType").toString());
            alertStatisticsVo.setMonth(map.get("monthTime").toString());
            alertStatisticsVo.setNumber(Integer.parseInt(map.get("aCount").toString()));
            list.add(alertStatisticsVo);
        }
        return list.stream().collect(Collectors.groupingBy(AlertStatisticsVO::getType));
    }

    public Date getMaxDate() {
        IPage<DemPoliceInfo> page = new Page<>(1, 1);
        LambdaQueryWrapper<DemPoliceInfo> maxQuery = Wrappers.lambdaQuery();
        maxQuery.isNotNull(DemPoliceInfo::getCreateTime)
                .orderByDesc(DemPoliceInfo::getCreateTime);
        IPage<DemPoliceInfo> maxPage = this.page(page, maxQuery);
        LocalDateTime localDateTime = maxPage.getRecords().get(0).getCreateTime();
        Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
        Date date = Date.from(instant);
        return date;
    }

    public Date getMinDate() {
        IPage<DemPoliceInfo> page = new Page<>(1, 1);
        LambdaQueryWrapper<DemPoliceInfo> minQuery = Wrappers.lambdaQuery();
        minQuery.isNotNull(DemPoliceInfo::getCreateTime)
                .orderByAsc(DemPoliceInfo::getCreateTime);
        IPage<DemPoliceInfo> minPage = this.page(page, minQuery);
        LocalDateTime localDateTime = minPage.getRecords().get(0).getCreateTime();
        Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
        Date date = Date.from(instant);
        return date;
    }

    public void perfect(Map<String, List<AlertStatisticsVO>> typeMap, Integer times, Integer subTime, Integer defaultTime) {
        for (String type : ALERT_TYPE) {
            if (!typeMap.containsKey(type)) {
                typeMap.put(type, new ArrayList<>());
            }
            perfectDate(typeMap.get(type), times, subTime, defaultTime);
        }
    }


    public void perfectDate(List<AlertStatisticsVO> list, Integer times, Integer subTime, Integer defaultTime) {
        for (int i = defaultTime; i < times; i++) {
            int finalI = i;
            list.stream()
                    .filter(asv -> Integer.parseInt(asv.getMonth()) == finalI)
                    .findFirst()
                    .map(asv -> {
                        asv.setAvg(Arith.div(asv.getNumber(), subTime));
                        return asv;
                    })
                    .orElseGet(() -> {
                        AlertStatisticsVO asv = new AlertStatisticsVO();
                        asv.setMonth(Integer.toString(finalI));
                        asv.setNumber(0);
                        asv.setAvg(0.00);
                        list.add(asv);
                        return asv;
                    });
        }
        list.sort(Comparator.comparingInt(x -> Integer.parseInt(x.getMonth())));
    }


}
