package com.tykj.dev.device.confirmcheck.entity.vo;

import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static java.util.stream.Collectors.toMap;

/**
 * CheckDeviceStatVo.
 *
 * @author Matrix <xhyrzldf@gmail.com>
 * @since 2020/8/15 at 7:18 下午
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@Slf4j
public class CheckDeviceStatVo implements Cloneable {

    /**
     * 装备型号
     */
    private String deviceModel;

    /**
     * 装备名称
     */
    private String deviceName;

    /**
     * 装备总数
     */
    private int deviceCount;


    /**
     * 核查该型号装备的各地区统计情况
     */
    private List<CheckAreaStatVo> areaStatList;

    public CheckDeviceStatVo(CheckDeviceStatVo vo) {
        this.deviceModel = vo.getDeviceModel();
        this.deviceName = vo.getDeviceName();
        this.deviceCount = vo.getDeviceCount();
        this.areaStatList = new ArrayList<>();
        for (CheckAreaStatVo areaStatVo : vo.getAreaStatList()) {
            this.areaStatList.add(new CheckAreaStatVo(areaStatVo));
        }
    }

    /**
     * 增加指定的装备数量
     *
     * @param count 装备数量
     */
    public CheckDeviceStatVo addDeviceCount(int count) {
        this.deviceCount += count;
        return this;
    }

    /**
     * 累加装备统计数据(相同型号&名字),并将其下面各个地区的统计信息合并(次级地区会强制更改为{cityName})
     * 多用于装备统计数据中既附带区又附带市的统计信息，想全部合并为市的操作(为了统一给省级单位查看统计数据)
     * <li>将所有地区全部改名为市级地区</li>
     * <li>合并所有数据</li>
     *
     * @param other    要合并的装备统计对象
     * @param cityName 统一后的地区名称
     * @return 合并后的装备统计对象，基于深拷贝的新对象
     */
    public CheckDeviceStatVo reduce(CheckDeviceStatVo other, String cityName, Integer cityStatId) {
        boolean flag = deviceModel.equals(other.deviceModel) && deviceName.equals(other.deviceName);
        if (!flag) {
            throw new IllegalArgumentException("累加的统计信息对象必须是相同型号与名字的装备");
        }
        CheckDeviceStatVo mergeVo = new CheckDeviceStatVo(this);
        mergeVo.deviceCount += other.deviceCount;
        //添加地区数据 -> 调用combine 合并地区数据
        mergeVo.areaStatList.addAll(other.getAreaStatList());
        return mergeVo.combine(cityName, cityStatId);
    }

    /**
     * 将相同型号的不同地区数据合并到一起
     *
     * @param other 要合并的装备统计信息
     * @return 合并后的装备统计信息对相关，基于深拷贝
     * @throws IllegalArgumentException 累加的统计信息对象必须是相同型号与名字的装备
     */
    public CheckDeviceStatVo reduce(CheckDeviceStatVo other) {
        boolean flag = deviceModel.equals(other.getDeviceModel()) && deviceName.equals(other.getDeviceName());
        if (!flag) {
            throw new IllegalArgumentException("累加的统计信息对象必须是相同型号与名字的装备");
        }

        CheckDeviceStatVo mergeVo = new CheckDeviceStatVo(this);
        mergeVo.deviceCount += other.deviceCount;

        // 如果源地区为空，则直接覆盖掉
        if (CollectionUtils.isEmpty(areaStatList)) {
            mergeVo.areaStatList = other.getAreaStatList();
            return mergeVo;
        }

        Map<String, CheckAreaStatVo> areaMap = mergeVo.areaStatList.stream()
                .collect(toMap(CheckAreaStatVo::getAreaName, Function.identity()));

        for (CheckAreaStatVo otherArea : other.getAreaStatList()) {
            //如果原来的没有则添加,否则累加
            areaMap.putIfAbsent(otherArea.getAreaName(), otherArea);
            areaMap.computeIfPresent(otherArea.getAreaName(), (k, v) -> v.reduce(otherArea));
        }

        mergeVo.areaStatList = new ArrayList<>(areaMap.values());
        return mergeVo;
    }

    /**
     * 将相同型号的不同地区数据合并到一起(清洁版)
     * 与普通reduce的区别是只会将统计结果合并，不会将统计指标合并，适用于预期结果+实际核查结果类型数据相加
     *
     * @param other 要合并的装备统计信息
     * @return 合并后的装备统计信息对相关，基于深拷贝
     * @throws IllegalArgumentException 累加的统计信息对象必须是相同型号与名字的装备
     */
    public CheckDeviceStatVo cleanReduce(CheckDeviceStatVo other) {
        boolean flag = deviceModel.equals(other.getDeviceModel()) && deviceName.equals(other.getDeviceName());
        if (!flag) {
            throw new IllegalArgumentException("累加的统计信息对象必须是相同型号且名称相同的装备");
        }

        CheckDeviceStatVo mergeVo = new CheckDeviceStatVo(this);

        // 如果源地区为空，则直接覆盖掉
        if (CollectionUtils.isEmpty(areaStatList)) {
            mergeVo.areaStatList = other.getAreaStatList();
            return mergeVo;
        }

        Map<String, CheckAreaStatVo> areaMap = mergeVo.areaStatList.stream()
                .collect(toMap(CheckAreaStatVo::getAreaName, Function.identity()));

        for (CheckAreaStatVo otherArea : other.getAreaStatList()) {
            //如果原来的没有则添加,否则累加
            areaMap.putIfAbsent(otherArea.getAreaName(), otherArea);
            areaMap.computeIfPresent(otherArea.getAreaName(), (k, v) -> v.cleanReduce(otherArea));
        }

        mergeVo.areaStatList = new ArrayList<>(areaMap.values());
        return mergeVo;
    }

    /**
     * 将当前 {@link CheckDeviceStatVo} 对象里的所有地区数据统一改名并合并数据
     *
     * @param newAreaName 要更新的地区名称
     * @param statId      要重新绑定的stat统计账单id,多为市级的账单统计id
     * @return 改名&合并地区数据后的统计对象，基于深拷贝的新对象
     */
    public CheckDeviceStatVo combine(String newAreaName, Integer statId) {
        CheckDeviceStatVo mergeVo = new CheckDeviceStatVo(this);
        CheckAreaStatVo mergedAreaStat = mergeVo.getAreaStatList().stream()
                .map(v -> v.setAreaName(newAreaName))
                .reduce((vo1, vo2) -> vo1.combine(vo2, statId))
                .orElse(new CheckAreaStatVo());
        mergeVo.setAreaStatList(Lists.newArrayList(mergedAreaStat));
        return mergeVo;
    }


}
