package com.kym.service.awoara.event.handle;

import com.kym.entity.MonitorLog;
import com.kym.entity.WashDevice;
import com.kym.entity.awoara.DeviceStatusObject;
import com.kym.entity.awoara.MessageBody;
import com.kym.entity.common.RedisKeys;
import com.kym.service.FaultNotificationService;
import com.kym.service.MonitorLogService;
import com.kym.service.WashDeviceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;

/**
 * 设备上下线状态变更处理
 *
 * @author skyline
 */
@Slf4j
public class DeviceStatusEventHandler implements AwoaraEventHandler<DeviceStatusObject> {

    private final WashDeviceService washDeviceService;
    private final MonitorLogService monitorLogService;
    private final StringRedisTemplate stringRedisTemplate;
    private final FaultNotificationService faultNotificationService;

    public DeviceStatusEventHandler(WashDeviceService washDeviceService,
                                    MonitorLogService monitorLogService,
                                    StringRedisTemplate stringRedisTemplate,
                                    FaultNotificationService faultNotificationService) {
        this.washDeviceService = washDeviceService;
        this.monitorLogService = monitorLogService;
        this.stringRedisTemplate = stringRedisTemplate;
        this.faultNotificationService = faultNotificationService;
    }

    @Override
    @Transactional
    public void handle(MessageBody<DeviceStatusObject> message) {
        log.info("DeviceStatusEventHandler: {}", message);

        var statusData = message.getPayload().getData();
        var status = statusData.getStatus();
        var productKey = statusData.getProductKey();
        var deviceName = statusData.getDeviceName();

        var device = washDeviceService.lambdaQuery()
                .eq(WashDevice::getProductKey, productKey)
                .eq(WashDevice::getDeviceName, deviceName)
                .one();

        if (device == null) {
            log.warn("设备状态变更：未找到设备 productKey={}, deviceName={}", productKey, deviceName);
            return;
        }

        var deviceId = device.getId().toString();
        var offlineKey = RedisKeys.OFFLINE + deviceId;

        if ("offline".equals(status)) {
            handleOffline(device, deviceName, offlineKey);
        } else if ("online".equals(status)) {
            handleOnline(device, deviceName, offlineKey);
        }
    }

    private void handleOffline(WashDevice device, String deviceName, String offlineKey) {
        log.info("设备离线: deviceId={}, deviceName={}", device.getId(), deviceName);

        // 更新设备状态为故障
        washDeviceService.lambdaUpdate()
                .set(WashDevice::getState, WashDevice.STATE_故障)
                .eq(WashDevice::getId, device.getId())
                .update();

        stringRedisTemplate.opsForValue().set(offlineKey,
                String.valueOf(System.currentTimeMillis()),
                Duration.ofHours(24));

        MonitorLog logEntry = new MonitorLog()
                .setStationId(device.getStationId())
                .setType(2)
                .setSn(deviceName)
                .setOfflineTime(LocalDateTime.now())
                .setOfflineStatus(0)
                .setIsNotice(MonitorLog.IS_RECOVER_未恢复)
                .setIsRecover(MonitorLog.IS_RECOVER_未恢复);
        monitorLogService.save(logEntry);

        faultNotificationService.handleOfflineFault(device.getStationId(), deviceName);
    }

    private void handleOnline(WashDevice device, String deviceName, String offlineKey) {
        log.info("设备上线: deviceId={}, deviceName={}", device.getId(), deviceName);

        // 更新设备状态为空闲
        washDeviceService.lambdaUpdate()
                .set(WashDevice::getState, WashDevice.STATE_空闲)
                .eq(WashDevice::getId, device.getId())
                .update();

        stringRedisTemplate.delete(offlineKey);

        var unrecoveredLog = monitorLogService.lambdaQuery()
                .eq(MonitorLog::getSn, deviceName)
                .eq(MonitorLog::getIsRecover, MonitorLog.IS_RECOVER_未恢复)
                .orderByDesc(MonitorLog::getOfflineTime)
                .last("limit 1")
                .one();

        if (unrecoveredLog != null) {
            monitorLogService.lambdaUpdate()
                    .set(MonitorLog::getIsRecover, MonitorLog.IS_RECOVER_已恢复)
                    .set(MonitorLog::getRecoverTime, LocalDateTime.now())
                    .eq(MonitorLog::getId, unrecoveredLog.getId())
                    .update();
        }

        faultNotificationService.handleOnlineRecovery(device.getStationId(), deviceName);
    }
}
