package com.jeequan.jeepay.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.jeequan.jeepay.core.constants.ApiCodeEnum;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.device.ISpeakerService;
import com.jeequan.jeepay.core.model.ApiRes;
import com.jeequan.jeepay.core.model.device.ZwParams;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.service.mapper.MchStoreDeviceMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 商户门店设备配置表 服务实现类
 * </p>
 *
 * @author [mybatis plus generator]
 * @since 2021-12-28
 */
@Service
public class MchStoreDeviceService extends ServiceImpl<MchStoreDeviceMapper, MchStoreDevice> {

    @Autowired MchQrcodeCardService mchQrcodeCardService;
    @Autowired MchQrcDeviceRelaService mchQrcDeviceRelaService;
    @Autowired DeviceProvideConfigService deviceProvideConfigService;
    @Autowired private MchAppService mchAppService;
    @Autowired private MchStoreService mchStoreService;
    @Autowired private MchInfoService mchInfoService;
    @Autowired private MchStoreDeviceMapper mchStoreDeviceMapper;

    /**
     * 查询可用的播报、打印设备
     */
    public List<JSONObject> selectAvailableDeviceList(String mchNo, String storeId, List<Byte> deviceTypeList) {

        Map<String, Object> params = new HashMap<>();
        if (StringUtils.isNotBlank(mchNo)) params.put("mchNo", mchNo);
        if (storeId != null) params.put("storeId", storeId);
        if (deviceTypeList != null && deviceTypeList.size() > 0) params.put("deviceTypeList", deviceTypeList);

        return baseMapper.selectAvailableDeviceList(params);

    }

    public IPage<MchStoreDevice> selectPage(IPage iPage, MchStoreDevice mchStoreDevice) {
        // 获取查询时间请求参数  [开始时间,  结束时间]
        Date[] queryDateRangeArray = mchStoreDevice.buildQueryDateRange();
        if(queryDateRangeArray[0] != null){
            mchStoreDevice.setFirstDate(queryDateRangeArray[0]);
        }
        if(queryDateRangeArray[1] != null){
            mchStoreDevice.setLastDate(queryDateRangeArray[1]);
        }

        IPage<MchStoreDevice> page = mchStoreDeviceMapper.getSelectPage(iPage, mchStoreDevice);
        // 添加厂商配置信息
        if (CollUtil.isNotEmpty(page.getRecords())) {
            List<Long> configIdList = page.getRecords().stream().map(MchStoreDevice::getConfigId).collect(Collectors.toList());
            List<DeviceProvideConfig> configList = deviceProvideConfigService.list(DeviceProvideConfig.gw()
                    .select(DeviceProvideConfig::getConfigId, DeviceProvideConfig::getConfigDesc).in(DeviceProvideConfig::getConfigId, configIdList));
            for (MchStoreDevice device : page.getRecords()) {
                for (DeviceProvideConfig provideConfig : configList) {
                    if (provideConfig.getConfigId().equals(device.getConfigId())) {
                        device.addExt("configDesc", provideConfig.getConfigDesc());
                    }
                }
                //转换json对象
                if (StringUtils.isNotEmpty(device.getBizConfigParams())){
                    String bizConfigParams = device.getBizConfigParams();
                    try {
                        JSONObject jsonBizConfigParams = JSONObject.parseObject(bizConfigParams);
                        device.setJsonBizConfigParams(jsonBizConfigParams);
                    }catch (Exception e){
                        device.setJsonBizConfigParams(new JSONObject());
                    }
                }
                //如果绑定的是马牌类型
                if(CS.YES == device.getBindType()){
                    List<MchQrcDeviceRela> relaList = mchQrcDeviceRelaService.list(MchQrcDeviceRela.gw().eq(MchQrcDeviceRela::getDeviceId, device.getDeviceId()));
                    if (!CollectionUtils.isEmpty(relaList)) {
                        device.addExt("qrcIdList", relaList.stream().map(MchQrcDeviceRela::getQrcId).collect(Collectors.toList()));
                    }
                }
                //手机号脱敏
                if (StringUtils.isNotEmpty(device.getMchServicePhone())){
                    device.setMchServicePhone(DesensitizedUtil.mobilePhone(device.getMchServicePhone()));
                }
                if (StringUtils.isNotEmpty(device.getMchUserPhone())){
                    device.setMchUserPhone(DesensitizedUtil.mobilePhone(device.getMchUserPhone()));
                }
            }
        }

        return page;
    }

    /**
     * 解绑设备
     */
    @Transactional
    public void unbind(MchStoreDevice dbRecord) {

        // 解绑设备
        boolean result = unbindByIds(Collections.singletonList(dbRecord.getDeviceId()));
        if(!result) {
            throw new BizException("解绑失败");
        }

        // 解绑初始绑定的码牌
        if (dbRecord.getDeviceType() != MchStoreDevice.DEVICE_TYPE_SPEAKER || dbRecord.getBindQrcId() == null) {
            sendBindMsg(dbRecord,false);
            return;
        }
        result = mchQrcodeCardService.unbindByIds(Collections.singletonList(dbRecord.getBindQrcId()));
        if(!result) {
            throw new BizException("解绑失败");
        }

        // 删除该云喇叭全部关联关系
        if (dbRecord.getDeviceType() == MchStoreDevice.DEVICE_TYPE_SPEAKER) {
            result = mchQrcDeviceRelaService.removeByDeviceId(dbRecord.getDeviceId());
            if(!result) {
                throw new BizException("解绑失败");
            }
        }
        sendBindMsg(dbRecord,false);
    }

    public void sendBindMsg(MchStoreDevice dbRecord,Boolean flag) {
        DeviceProvideConfig provideConfig = deviceProvideConfigService.getById(dbRecord.getConfigId());
        if (provideConfig == null || provideConfig.getState() != CS.YES) {
            throw new BizException("设备厂商未配置或已禁用");
        }
        ISpeakerService speaker = SpringBeansUtil.getBean(provideConfig.getProvider() + "SpeakerService", ISpeakerService.class);
        if(speaker != null){
            JSONObject deviceParams = new JSONObject();
            deviceParams.put("deviceParams", dbRecord.getDeviceParams());
            deviceParams.put("providerParams", provideConfig.getProviderParams());
            JSONObject extParams = new JSONObject(1);
            extParams.put("bindState",flag ? MchStoreDevice.YES_BIND : MchStoreDevice.NO_BIND);
            deviceParams.put("extParams",extParams.toJSONString());
            speaker.bindSend(deviceParams,false);
        }
    }
    /**
     * 解绑设备
     */
    public boolean unbindByIds(List<Long> deviceIdList) {
        LambdaUpdateWrapper<MchStoreDevice> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(MchStoreDevice::getMchNo, "")
                .set(MchStoreDevice::getStoreId, 0L)
                .set(MchStoreDevice::getAppId, "")
                .set(MchStoreDevice::getBindState, CS.NO)
                .set(MchStoreDevice::getUnbindTime, new Date())
                .in(MchStoreDevice::getDeviceId, deviceIdList);

        return this.update(updateWrapper);
    }

    /**
     * 划拨设备
     */
    @Transactional
    public boolean allotDevice(Boolean isAllotQrcSync, String agentNo, List<Long> deviceIdList, List<String> mchNoList, List<String> subAgentNoList) {

        // 筛选已绑定的设备
        // 商户属于要划拨的服务商，可划拨；反之不可划拨
        List<MchStoreDevice> bindList = this.list(MchStoreDevice.gw()
                .in(MchStoreDevice::getDeviceId, deviceIdList)
                .eq(MchStoreDevice::getBindState, CS.YES)
        );

        // 选择的设备存在已绑定设备
        if (CollUtil.isNotEmpty(bindList)) {

            if (CollUtil.isEmpty(mchNoList)) {
                throw new BizException("设备已绑定商户，且绑定的商户不属于要划拨的服务商，请重新选择");
            }

            for (MchStoreDevice mchStoreDevice : bindList) {
                if (!mchNoList.contains(mchStoreDevice.getMchNo())) {
                    throw new BizException("设备号【" + mchStoreDevice.getDeviceNo() + "】已绑定商户，且绑定的商户不属于要划拨的服务商，请重新选择");
                }
            }
        }

        boolean result;

        // 1、同步划拨绑定的码牌
        if (isAllotQrcSync) {
            // 要同步划拨码牌的 设备集合
            List<MchStoreDevice> deviceList = this.list(MchStoreDevice.gw()
                    .in(MchStoreDevice::getDeviceId, deviceIdList)
                    .isNotNull(MchStoreDevice::getBindQrcId)
                    .ne(MchStoreDevice::getBindQrcId, 0)
            );

            if (CollUtil.isNotEmpty(deviceList)) {
                // 要同步划拨的码牌ID集合
                List<Long> qrcIdList = deviceList.stream().map(MchStoreDevice::getBindQrcId).collect(Collectors.toList());

                result = mchQrcodeCardService.allotQrc(false, agentNo, qrcIdList, mchNoList, subAgentNoList);
                if (!result) {
                    throw new BizException(ApiCodeEnum.SYS_OPERATION_FAIL_UPDATE);
                }
            }
        }


        // 2、划拨设备
        result = this.update(new LambdaUpdateWrapper<MchStoreDevice>()
                .set(MchStoreDevice::getAgentNo, agentNo)
                .in(MchStoreDevice::getDeviceId, deviceIdList)
                .and(i ->{
                    i.in(CollUtil.isNotEmpty(subAgentNoList), MchStoreDevice::getAgentNo, subAgentNoList)
                        .or().isNull(MchStoreDevice::getAgentNo)
                        .or().eq(MchStoreDevice::getAgentNo, "");
                })

        );
        if (!result) {
            throw new BizException(ApiCodeEnum.SYS_OPERATION_FAIL_UPDATE);
        }

        return true;
    }

    /**
     * 收回设备
     * @param isRecoverQrcSync 是否同步收回码牌
     * @param deviceIdList 收回的设备ID集合
     * @param subAgentNoList 可执行操作的子服务商号集合，服务商只能操作自己的下级代理
     * @param recoverAgentNo 收回到某个服务商下
     */
    @Transactional
    public boolean recoverDevice(Boolean isRecoverQrcSync, List<Long> deviceIdList, List<String> subAgentNoList, String recoverAgentNo) {

        boolean result;

        // 1、同步收回绑定的码牌
        if (isRecoverQrcSync) {
            // 要同步收回码牌的 设备集合
            List<MchStoreDevice> deviceList = this.list(MchStoreDevice.gw().in(MchStoreDevice::getDeviceId, deviceIdList));

            // 要同步收回的码牌ID集合
            List<Long> qrcIdList = new LinkedList<>();
            deviceList.forEach(item -> {
                if (item.getBindQrcId() != null) {
                    qrcIdList.add(item.getBindQrcId());
                }
            });

            if (CollUtil.isNotEmpty(qrcIdList)) {
                result = mchQrcodeCardService.recoverQrc(false, qrcIdList, subAgentNoList, recoverAgentNo);
                if (!result) {
                    throw new BizException(ApiCodeEnum.SYS_OPERATION_FAIL_UPDATE);
                }
            }
        }

        // 2、收回设备
        result = this.update(new LambdaUpdateWrapper<MchStoreDevice>()
                .set(MchStoreDevice::getAgentNo, StringUtils.isBlank(recoverAgentNo) ? "" : recoverAgentNo)
                .in(MchStoreDevice::getDeviceId, deviceIdList)
                .in(CollUtil.isNotEmpty(subAgentNoList), MchStoreDevice::getAgentNo, subAgentNoList)
        );
        if (!result) {
            throw new BizException(ApiCodeEnum.SYS_OPERATION_FAIL_UPDATE);
        }

        return true;
    }

    /**
     * 绑定云喇叭
     */
    @Transactional
    public boolean bindSpeakerDevice(MchStoreDevice mchStoreDevice, Long bindQrcId, List<Long> qrcList) {

        boolean result;

        // 1、修改 && 绑定云喇叭
        result = updateById(mchStoreDevice);
        if (!result) {
            throw new BizException("绑定失败");
        }

        // 2、初始配置的码牌  同步绑定到设备的商户及门店下
        if (bindQrcId != null) {

            // 添加初始绑定码牌ID
            if (!qrcList.contains(bindQrcId)) {
                qrcList.add(bindQrcId);
            }

            result = mchQrcodeCardService.bindByIds(qrcList, mchStoreDevice.getMchNo(), mchStoreDevice.getStoreId(), mchStoreDevice.getAppId(), mchStoreDevice.getAgentNo());
            if (!result) {
                throw new BizException("绑定失败");
            }
        }

        // 3、云喇叭绑定码牌
        if (null != mchStoreDevice.getBindType() && mchStoreDevice.getBindType() == MchStoreDevice.DEVICE_BIND_TYPE_QRC) {

            if (CollectionUtils.isEmpty(qrcList)) {
                throw new BizException("请选择要绑定的码牌！");
            }

            // 检查码牌ID和设备ID是否重复
            // int count = mchQrcDeviceRelaService.count(MchQrcDeviceRela.gw()
            //         .eq(MchQrcDeviceRela::getDeviceId, mchStoreDevice.getDeviceId())
            //         .in(MchQrcDeviceRela::getQrcId, qrcList)
            // );
            // if (count > 0) {
            //     throw new BizException("码牌已绑定至其他设备！");
            // }

            mchQrcDeviceRelaService.remove(MchQrcDeviceRela.gw().eq(MchQrcDeviceRela::getDeviceId, mchStoreDevice.getDeviceId()));

            List<MchQrcDeviceRela> updateList = new LinkedList<>();

            for (Long qrcId:qrcList) {
                MchQrcDeviceRela mchQrcDeviceRela = new MchQrcDeviceRela();
                mchQrcDeviceRela.setDeviceId(mchStoreDevice.getDeviceId()).setQrcId(qrcId);
                updateList.add(mchQrcDeviceRela);
            }
            result = mchQrcDeviceRelaService.saveBatch(updateList);
            if (!result) {
                throw new BizException("绑定失败");
            }
        }
        if(StringUtils.isBlank(mchStoreDevice.getDeviceParams())){
            mchStoreDevice = getById(mchStoreDevice.getDeviceId());
        }
        sendBindMsg(mchStoreDevice,mchStoreDevice.getBindState() == CS.YES);
        return true;
    }

    /** 根据初始绑定码牌ID获取 云喇叭设备信息 */
    public MchStoreDevice getByBindQrcId(Long qrcId) {
        List<MchStoreDevice> deviceList = this.list(MchStoreDevice.gw().eq(MchStoreDevice::getBindQrcId, qrcId));
        if (CollectionUtils.isEmpty(deviceList)) {
            return null;
        }

        return deviceList.get(0);
    }

    public MchStoreDevice getByUniqueKey(String provider, Byte deviceType, String deviceNo) {
        List<MchStoreDevice> list = this.list(MchStoreDevice.gw()
                .eq(MchStoreDevice::getProvider, provider)
                .eq(MchStoreDevice::getDeviceType, deviceType)
                .eq(MchStoreDevice::getDeviceNo, deviceNo)
        );
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        return list.get(0);
    }

    /** 批量新增设备 */
    public ApiRes addDeviceBatch(MchStoreDevice mchStoreDevice, String[] deviceNoArr) {
        // 设备号去重
        List<String> deviceNoList = Arrays.stream(deviceNoArr).distinct().collect(Collectors.toList());
        // 同厂商  同设备类型   设备号唯一
        List<MchStoreDevice> dbDeviceList = this.list(MchStoreDevice.gw()
                .eq(MchStoreDevice::getProvider, mchStoreDevice.getProvider())
                .eq(MchStoreDevice::getDeviceType, mchStoreDevice.getDeviceType())
                .in(CollUtil.isNotEmpty(deviceNoList), MchStoreDevice::getDeviceNo, deviceNoList));
        if (CollUtil.isNotEmpty(dbDeviceList)) {
            throw new BizException("设备号【"+CollUtil.join(dbDeviceList.stream().map(MchStoreDevice::getDeviceNo).collect(Collectors.toList()), ", ")+"】重复，请检查！");
        }

        List<MchStoreDevice> list = new LinkedList<>();
        for (String deviceNo : deviceNoList) {
            MchStoreDevice newRecord = new MchStoreDevice();
            BeanUtil.copyProperties(mchStoreDevice, newRecord);
            newRecord.setDeviceName(RandomUtil.randomString(4));

            JSONObject deviceJSON = new JSONObject();
            deviceJSON.put("deviceNo", deviceNo);
            newRecord.setDeviceParams(deviceJSON.toJSONString());
            newRecord.setDeviceNo(deviceNo);

            list.add(newRecord);
        }

        if (this.saveBatch(list)) {
            return ApiRes.ok();
        }
        return ApiRes.fail(ApiCodeEnum.SYS_OPERATION_FAIL_CREATE);
    }

    /** 新增智网打印设备 **/
    public ApiRes addDeviceByZw(MchStoreDevice mchStoreDevice) {

        ZwParams.DeviceParams deviceParams = JSONObject.parseObject(mchStoreDevice.getDeviceParams(), ZwParams.DeviceParams.class);

        // 同厂商  同设备类型   设备号唯一
        List<MchStoreDevice> dbDeviceList = this.list(MchStoreDevice.gw()
                .eq(MchStoreDevice::getProvider, mchStoreDevice.getProvider())
                .eq(MchStoreDevice::getDeviceType, mchStoreDevice.getDeviceType())
                .eq(MchStoreDevice::getDeviceNo, deviceParams.getDeviceNo()));
        if (CollUtil.isNotEmpty(dbDeviceList)) {
            throw new BizException("设备号【"+CollUtil.join(dbDeviceList.stream().map(MchStoreDevice::getDeviceNo).collect(Collectors.toList()), ", ")+"】重复，请检查！");
        }

        MchStoreDevice newRecord = new MchStoreDevice();
        BeanUtil.copyProperties(mchStoreDevice, newRecord);
        newRecord.setDeviceName(RandomUtil.randomString(4));

        newRecord.setDeviceParams(JSON.toJSONString(deviceParams));
        newRecord.setDeviceNo(deviceParams.getDeviceNo());
        if (this.save(newRecord)) {
            return ApiRes.ok();
        }
        return ApiRes.fail(ApiCodeEnum.SYS_OPERATION_FAIL_CREATE);
    }

    /**
     * 获取设备的绑定状态
     * @param deviceSn
     * @param deviceType
     * @param provider
     * @return
     */
    public MchStoreDevice getDeviceInfo(String deviceSn, Byte deviceType, String provider) {
        MchStoreDevice device = baseMapper.selectOne(MchStoreDevice.gw().eq(MchStoreDevice::getDeviceNo, deviceSn).eq(MchStoreDevice::getDeviceType, deviceType).eq(MchStoreDevice::getProvider, provider));
        if(device == null){
            throw new BizException("设备号异常或当前设备还未入库");
        }
        return device;
    }
}
