package org.dromara.app.handler;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.app.mqtt.MqttTopicHandler;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.stereotype.Component;

import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 设备遗嘱离线状态处理器，匹配 /{deviceIdentity}/publish/status。
 * <p>
 * deviceIdentity 支持设备编号；设备遗嘱消息允许使用 MAC 地址，处理前统一解析为设备编号。
 * 在线状态由电量上报刷新；本处理器仅通过 LWT status=offline 标记异常离线。
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DeviceStatusHandler implements MqttTopicHandler {

    private static final Pattern PATTERN = Pattern.compile("^/([^/]+)/publish/status$");
    private static final String OFFLINE_REASON = "设备 MQTT 状态离线";

    private final DeviceIdentityResolver deviceIdentityResolver;
    private final MqttDeviceStatusService deviceStatusService;
    private final ObjectMapper objectMapper;

    @Override
    public Pattern topicPattern() {
        return PATTERN;
    }

    @Override
    public void handle(String deviceIdentity, String payload) {
        handle(deviceIdentity, payload, false);
    }

    @Override
    public void handle(String deviceIdentity, String payload, boolean retained) {
        Map<String, Object> body = parsePayload(payload);
        String deviceNo = resolveDeviceNo(deviceIdentity, body);
        if (deviceNo == null) {
            log.warn("[MQTT] 设备状态上报未找到设备 时间={} 设备标识={} 消息体={}",
                HandlerLogTime.now(), deviceIdentity, payload);
            return;
        }

        String status = parseStatus(payload, body);
        if ("online".equals(status) || "1".equals(status)) {
            log.debug("[MQTT] 忽略设备主动在线状态，在线状态由电量心跳维护 时间={} 设备编号={} 消息体={}",
                HandlerLogTime.now(), deviceNo, payload);
            return;
        }
        if ("offline".equals(status) || "0".equals(status)) {
            if (retained) {
                log.warn("[MQTT] 忽略 retained 离线状态 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
                return;
            }
            deviceStatusService.markOffline(deviceNo, OFFLINE_REASON);
            log.info("[MQTT] 设备状态离线 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
            return;
        }

        log.warn("[MQTT] 设备状态上报状态未知 时间={} 设备编号={} 消息体={}",
            HandlerLogTime.now(), deviceNo, payload);
    }

    private String resolveDeviceNo(String deviceIdentity, Map<String, Object> body) {
        String deviceNo = deviceIdentityResolver.resolveDeviceNo(deviceIdentity);
        if (deviceNo != null || body == null) {
            return deviceNo;
        }
        Object deviceMac = body.get("deviceMac");
        if (deviceMac == null || StringUtils.isBlank(String.valueOf(deviceMac))) {
            return null;
        }
        return deviceIdentityResolver.resolveDeviceNo(String.valueOf(deviceMac));
    }

    private Map<String, Object> parsePayload(String payload) {
        if (StringUtils.isBlank(payload)) {
            return null;
        }
        String trimmed = payload.trim();
        if (!trimmed.startsWith("{")) {
            return null;
        }
        try {
            return objectMapper.readValue(trimmed, new TypeReference<Map<String, Object>>() {
            });
        } catch (Exception e) {
            log.warn("[MQTT] 设备状态上报 JSON 格式错误 时间={} 消息体={}", HandlerLogTime.now(), payload, e);
            return null;
        }
    }

    private String parseStatus(String payload, Map<String, Object> body) {
        if (body == null) {
            if (StringUtils.isBlank(payload)) {
                return null;
            }
            String trimmed = payload.trim();
            return trimmed.startsWith("{") ? null : trimmed.toLowerCase(Locale.ROOT);
        }

        Object status = body.get("status");
        if (status != null) {
            return String.valueOf(status).trim().toLowerCase(Locale.ROOT);
        }
        Object offline = body.get("offline");
        if (offline != null && "true".equalsIgnoreCase(String.valueOf(offline).trim())) {
            return "offline";
        }
        return null;
    }
}
