package org.dromara.app.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.dromara.app.domain.AppDevice;
import org.dromara.app.mapper.AppDeviceMapper;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
@Tag("dev")
class DeviceStatusHandlerTest {

    @Mock
    private DeviceIdentityResolver deviceIdentityResolver;

    @Mock
    private MqttDeviceStatusService deviceStatusService;

    @Mock
    private AppDeviceMapper appDeviceMapper;

    @Test
    void topicPatternMatchesDeviceStatusTopic() {
        DeviceStatusHandler handler = newHandler();

        assertThat(handler.topicPattern().matcher("/D01/publish/status").matches()).isTrue();
        assertThat(handler.topicPattern().matcher("/D01/publish/power").matches()).isFalse();
    }

    @Test
    void handleIgnoresOnlineStatusBecausePowerReportMarksDeviceOnline() {
        DeviceStatusHandler handler = newHandler();
        when(deviceIdentityResolver.resolveDeviceNo("D01")).thenReturn("D01");

        handler.handle("D01", "online");

        verify(deviceStatusService, never()).markOnline(anyString());
        verify(deviceStatusService, never()).markOffline(anyString(), anyString());
    }

    @Test
    void handleMarksDeviceOfflineFromJsonPayload() {
        DeviceStatusHandler handler = newHandler();
        when(deviceIdentityResolver.resolveDeviceNo("D01")).thenReturn("D01");

        handler.handle("D01", "{\"status\":\"offline\"}");

        verify(deviceStatusService).markOffline("D01", "设备 MQTT 状态离线");
        verify(deviceStatusService, never()).markOnline(anyString());
    }

    @Test
    void handleMarksDeviceOfflineFromOfflineFlagAndPayloadMac() {
        DeviceStatusHandler handler = newHandler();
        when(deviceIdentityResolver.resolveDeviceNo("unknown-topic-identity")).thenReturn(null);
        when(deviceIdentityResolver.resolveDeviceNo("DC:DA:0C:FA:29:5E")).thenReturn("D01");

        handler.handle("unknown-topic-identity", "{\"deviceMac\":\"DC:DA:0C:FA:29:5E\",\"offline\":\"true\"}");

        verify(deviceIdentityResolver).resolveDeviceNo("DC:DA:0C:FA:29:5E");
        verify(deviceStatusService).markOffline("D01", "设备 MQTT 状态离线");
        verify(deviceStatusService, never()).markOnline(anyString());
    }

    @Test
    void handleResolvesLastWillTopicMacToDeviceNo() {
        String macAddress = "DC:DA:0C:FA:29:5E";
        AppDevice device = new AppDevice();
        device.setDeviceNo("D01");
        when(appDeviceMapper.selectById(macAddress)).thenReturn(null);
        when(appDeviceMapper.selectByMac("dc:da:0c:fa:29:5e")).thenReturn(device);
        DeviceIdentityResolver resolver = new DeviceIdentityResolver(appDeviceMapper);
        DeviceStatusHandler handler = new DeviceStatusHandler(resolver, deviceStatusService, new ObjectMapper());

        handler.handle(macAddress, "{\"status\":\"offline\"}", false);

        verify(appDeviceMapper).selectByMac("dc:da:0c:fa:29:5e");
        verify(deviceStatusService).markOffline("D01", "设备 MQTT 状态离线");
        verify(deviceStatusService, never()).markOffline(macAddress, "设备 MQTT 状态离线");
    }

    @Test
    void handleDoesNotUpdateStatusWhenLastWillMacIsUnknown() {
        String macAddress = "DC:DA:0C:FA:29:5E";
        when(appDeviceMapper.selectById(macAddress)).thenReturn(null);
        when(appDeviceMapper.selectByMac("dc:da:0c:fa:29:5e")).thenReturn(null);
        DeviceIdentityResolver resolver = new DeviceIdentityResolver(appDeviceMapper);
        DeviceStatusHandler handler = new DeviceStatusHandler(resolver, deviceStatusService, new ObjectMapper());

        handler.handle(macAddress, "{\"status\":\"offline\"}", false);

        verify(deviceStatusService, never()).markOffline(anyString(), anyString());
        verify(deviceStatusService, never()).markOnline(anyString());
    }

    @Test
    void handleIgnoresUnknownStatusPayload() {
        DeviceStatusHandler handler = newHandler();
        when(deviceIdentityResolver.resolveDeviceNo("D01")).thenReturn("D01");

        handler.handle("D01", "{\"status\":\"sleeping\"}");

        verify(deviceStatusService, never()).markOnline(anyString());
        verify(deviceStatusService, never()).markOffline(anyString(), anyString());
    }

    private DeviceStatusHandler newHandler() {
        return new DeviceStatusHandler(deviceIdentityResolver, deviceStatusService, new ObjectMapper());
    }
}
