package org.dromara.app.handler;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.app.domain.AppDevice;
import org.dromara.app.domain.bo.AppDeviceBo;
import org.dromara.app.domain.mqtt.DeviceCommand;
import org.dromara.app.mapper.AppDeviceMapper;
import org.dromara.app.mqtt.MqttTopicHandler;
import org.dromara.app.service.IAppDeviceService;
import org.dromara.app.service.IDeviceCommandPublisher;
import org.dromara.common.json.utils.JsonUtils;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Component;

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

/**
 * 设备注册处理器，匹配 /{deviceMac}/publish/register。
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class
DeviceRegisterHandler implements MqttTopicHandler {

    private static final Pattern PATTERN = Pattern.compile("^/([^/]+)/publish/register$");

    private final IAppDeviceService appDeviceService;
    private final AppDeviceMapper appDeviceMapper;
    private final ObjectProvider<IDeviceCommandPublisher> commandPublisherProvider;
    private final DeviceIdentityResolver deviceIdentityResolver;
    private final MqttDeviceStatusService deviceStatusService;

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

    @Override
    public void handle(String deviceIdentity, String payload) {
        try {
            Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class);
            AppDevice exists = deviceIdentityResolver.resolve(deviceIdentity);
            String normalizedDeviceMac = resolveDeviceMac(deviceIdentity, dto, exists);
            if (exists == null && normalizedDeviceMac != null) {
                exists = appDeviceMapper.selectByMac(normalizedDeviceMac);
            }
            if (exists == null) {
                log.warn("[MQTT] 设备注册失败，设备标识未入库 时间={} 设备标识={} 消息体={}",
                    HandlerLogTime.now(), deviceIdentity, payload);
                return;
            }

            String deviceNo = exists.getDeviceNo();
            if (normalizedDeviceMac == null) {
                normalizedDeviceMac = deviceIdentityResolver.normalizeMacAddress(exists.getMacAddress());
            }
            AppDeviceBo device = buildRegisterDevice(deviceNo, normalizedDeviceMac, dto);
            appDeviceService.registerByMqtt(device);
            deviceStatusService.markOnline(deviceNo);
            sendDeviceNoToDevice(deviceNo, normalizedDeviceMac);
            log.info("[MQTT] 设备注册并上线 时间={} MAC={} 设备编号={} 消息体={}",
                HandlerLogTime.now(), normalizedDeviceMac, deviceNo, payload);
        } catch (Exception e) {
            log.error("[MQTT] 设备注册失败 时间={} 设备标识={} 消息体={}", HandlerLogTime.now(), deviceIdentity, payload, e);
        }
    }

    private AppDeviceBo buildRegisterDevice(String deviceNo, String deviceMac, Map<String, Object> dto) {
        AppDeviceBo device = new AppDeviceBo();
        device.setDeviceNo(deviceNo);
        device.setMacAddress(deviceMac);
        if (dto == null) {
            return device;
        }
        device.setDeviceInitName(valueAsString(dto.get("deviceName")));
        device.setPowerLevel(valueAsString(dto.get("powerLevel")));
        device.setDeviceEm(valueAsString(dto.get("deviceEm")));
        device.setDeviceSn(valueAsString(dto.get("deviceSn")));
        device.setFwVer(firstNotBlank(dto.get("version"), dto.get("fwVer")));
        return device;
    }

    private String resolveDeviceMac(String topicDeviceMac, Map<String, Object> dto, AppDevice exists) {
        String payloadMac = dto == null ? null : firstNotBlank(dto.get("deviceMac"), dto.get("macAddress"));
        if (payloadMac != null && !payloadMac.isBlank()) {
            return normalizeMacAddress(payloadMac);
        }
        if (exists != null && exists.getMacAddress() != null && !exists.getMacAddress().isBlank()) {
            return normalizeMacAddress(exists.getMacAddress());
        }
        return normalizeMacAddress(topicDeviceMac);
    }

    private String normalizeMacAddress(String macAddress) {
        if (macAddress == null) {
            return null;
        }
        String text = macAddress.trim();
        return text.isBlank() ? null : text.toLowerCase(Locale.ROOT);
    }

    private void sendDeviceNoToDevice(String deviceNo, String deviceMac) {
        IDeviceCommandPublisher commandPublisher = commandPublisherProvider.getIfAvailable();
        if (commandPublisher == null) {
            log.warn("[MQTT] 设备编号下发失败，未找到命令发布器 时间={} MAC={} 设备编号={}",
                HandlerLogTime.now(), deviceMac, deviceNo);
            return;
        }
        if (deviceMac == null) {
            log.warn("[MQTT] 设备编号下发失败，缺少设备 MAC 时间={} 设备编号={}",
                HandlerLogTime.now(), deviceNo);
            return;
        }

        try {
            DeviceCommand command = new DeviceCommand();
            command.setDeviceNo(deviceNo);
            command.setDeviceMac(deviceMac);
            command.setCommandType("registerDeviceNo");
            command.setTopic("/" + deviceMac.trim().toLowerCase(Locale.ROOT) + "/subscriber/cmd");
            command.getPayload().put("deviceNo", deviceNo);
            command.getPayload().put("deviceMac", deviceMac);

            String commandId = commandPublisher.send(command);
            log.info("[MQTT] 设备编号已下发 时间={} MAC={} 设备编号={} 命令编号={}",
                HandlerLogTime.now(), deviceMac, deviceNo, commandId);
        } catch (Exception e) {
            log.error("[MQTT] 设备编号下发失败 时间={} MAC={} 设备编号={}",
                HandlerLogTime.now(), deviceMac, deviceNo, e);
        }
    }

    private @Nullable String firstNotBlank(Object first, Object second) {
        String firstText = valueAsString(first);
        if (firstText != null && !firstText.isBlank()) {
            return firstText;
        }
        String secondText = valueAsString(second);
        return secondText != null && !secondText.isBlank() ? secondText : null;
    }

    private String valueAsString(Object value) {
        return value == null ? null : String.valueOf(value);
    }
}
