package com.zsc.edu.gateway.modules.iot.record.service.impl;

import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.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.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zsc.edu.gateway.modules.iot.device.entity.Device;
import com.zsc.edu.gateway.modules.iot.device.repo.DeviceRepository;
import com.zsc.edu.gateway.modules.iot.record.entity.RecordData;
import com.zsc.edu.gateway.modules.iot.record.entity.RecordDataStatusVo;
import com.zsc.edu.gateway.modules.iot.record.repo.RecordDataRepository;
import com.zsc.edu.gateway.modules.iot.record.service.RecordDataService;
import com.zsc.edu.gateway.modules.iot.tsl.entity.DataType;
import com.zsc.edu.gateway.modules.iot.tsl.entity.Event;
import com.zsc.edu.gateway.modules.iot.tsl.entity.EventLog;
import com.zsc.edu.gateway.modules.iot.tsl.entity.CompareParam;
import com.zsc.edu.gateway.modules.iot.tsl.repo.EventLogRepository;
import com.zsc.edu.gateway.modules.iot.tsl.repo.EventRepository;
import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 上报数据
 *
 * @author zhuang
 */
@AllArgsConstructor
@Service
public class RecordDataServiceImpl extends ServiceImpl<RecordDataRepository, RecordData> implements RecordDataService {

    @Resource
    private final DeviceRepository deviceRepository;
    @Resource
    private final EventRepository eventRepository;
    @Resource
    private final EventLogRepository eventLogRepository;

    /**
     * 处理上报数据并进行监控
     *
     * @param clientId
     * @param data
     * @return recordData
     */
    @Override
    @Transactional
    public RecordData recordData(String clientId, JSONObject data) {
        RecordData recordData = new RecordData();
        recordData.setClientId(clientId);
        recordData.setContent(data);
        recordData.setRecordTime(LocalDateTime.now());
        this.save(recordData);

        boolean isProcessed = processRecordData(recordData);
        updateDeviceState(clientId, !isProcessed);

        return recordData;
    }

    /**
     * 获取上报数据状态
     * @return RecordDataStatusVo
     */
    @Override
    public RecordDataStatusVo recordDataStatus() {
        LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);

        return RecordDataStatusVo.builder()
                .warningCount(baseMapper.countWarnings())
                .todayWarningCount(baseMapper.countTodayWarnings(todayStart))
                .recordCount(baseMapper.selectCount(new LambdaQueryWrapper<>()))
                .dataCount(baseMapper.selectCount(new LambdaQueryWrapper<RecordData>().isNotNull(RecordData::getContent)))
                .build();
    }

    /**
     * 查询上报数据
     * @param page 分页数据
     * @param clientId 设备的 clientId
     * @return IPage<RecordData>
     */
    @Override
    public IPage<RecordData> query(Page<RecordData> page, String clientId) {
        LambdaQueryWrapper<RecordData> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RecordData::getClientId, clientId);
        return baseMapper.selectPage(page, queryWrapper);
    }

    /**
     * 监控上报数据
     *
     * @param recordData 上报数据
     * @return
     */
    public boolean processRecordData(RecordData recordData) {
        // 根据 clientId 查询设备信息
        Device device = deviceRepository.findByClientId(recordData.getClientId());
        if (device == null) {
            return false;
        }

        // 获取产品下的所有事件
        List<Event> events = eventRepository.selectByProductId(device.getProductId(), true);
        if (events.isEmpty()) {
            throw new JSONException("该设备下事件并未启用！或者该设备下无定义事件！");
        }

        boolean processed = false;

        // 遍历每个事件
        for (Event event : events) {
            for (CompareParam param : event.getOutputs()) {
                boolean isProcessed = processParam(recordData, param, event);
                if (isProcessed) {
                    processed = true;
                    break; // 找到并处理完第一个参数后跳出内层循环
                }
            }
            if (processed) {
                break; // 如果已经处理过参数，跳出外层循环
            }
        }

        return processed;
    }

    /**
     * 处理数据
     *
     * @param recordData 上报数据
     * @param param      属性
     * @param event      事件
     */
    private boolean processParam(RecordData recordData, CompareParam param, Event event) {
        String identifier = param.getIdentifier();
        Object valueObj = recordData.getContent().get(identifier);
        if (valueObj == null) {
            throw new JSONException("上报数据中未找到属性：" + identifier);
        }
        DataType dataType = param.getDataType();
        String defaultValueStr = param.getDefaultValue();
        Object value = convertValue(valueObj.toString(), dataType);
        Object defaultValue = convertValue(defaultValueStr, dataType);
        //判断数值类型
        if (dataType == DataType.BOOLEAN) {
            value = ((Boolean) value) ? 1 : 0;
            defaultValue = ((Boolean) defaultValue) ? 1 : 0;
        }
        //进行监控
        return processComparison(recordData, param, event, (Number) defaultValue, (Number) value);
    }

    /**
     * 转换数值
     */
    private Object convertValue(String value, DataType dataType) {
        return switch (dataType) {
            case INT -> Integer.parseInt(value);
            case FLOAT -> Float.parseFloat(value);
            case DOUBLE -> Double.parseDouble(value);
            case BOOLEAN -> Boolean.parseBoolean(value);
            default -> throw new IllegalArgumentException("Unsupported data type: " + dataType);
        };
    }

    /**
     * 判断数值进行监控
     */
    private boolean processComparison(RecordData recordData, CompareParam param, Event event, Number defaultValue, Number value) {
        CompareParam.CompareType compareType = param.getCompareType();
        boolean result = switch (compareType) {
            case GT -> value.doubleValue() > defaultValue.doubleValue();
            case LT -> value.doubleValue() < defaultValue.doubleValue();
            case EQ -> value.doubleValue() == defaultValue.doubleValue();
            case GE -> value.doubleValue() >= defaultValue.doubleValue();
            case LE -> value.doubleValue() <= defaultValue.doubleValue();
        };
        if (result) {
            eventLogRepository.insert(new EventLog(null, event.getName(), recordData.getId(), recordData, LocalDateTime.now(), null, null));
            return true;
        }
        return false;
    }

    private void updateDeviceState(String clientId, boolean isDeviceState) {
        deviceRepository.update(new LambdaUpdateWrapper<Device>()
                .eq(Device::getClientId, clientId)
                .set(Device::getDeviceState, isDeviceState));
    }
}
