package org.dromara.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import org.dromara.app.domain.AppDevice;
import org.dromara.app.domain.bo.AppDeviceBo;
import org.dromara.app.domain.vo.AppDeviceVo;
import org.dromara.app.mapper.AppDeviceMapper;
import org.dromara.app.mapper.AppSchedulingDeviceMapper;
import org.dromara.app.mapper.AppWateringLogMapper;
import org.dromara.app.service.IAppWateringLogService;
import org.dromara.common.core.enums.UserType;
import org.dromara.common.satoken.utils.LoginHelper;
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.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.List;
import java.util.Map;

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

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

    @Mock
    private AppDeviceMapper appDeviceMapper;
    @Mock
    private AppSchedulingDeviceMapper schedulingDeviceMapper;
    @Mock
    private AppWateringLogMapper wateringLogMapper;
    @Mock
    private IAppWateringLogService wateringLogService;

    @Test
    void renderQrCodeImage_addsDeviceNoBelowQrCode() throws Exception {
        byte[] imageBytes = AppDeviceServiceImpl.renderQrCodeImage("{\"deviceNo\":\"D20260702001\"}", "D20260702001");

        BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes));

        assertThat(image.getWidth()).isEqualTo(300);
        assertThat(image.getHeight()).isEqualTo(320);
        assertThat(countNonWhitePixels(image, 300, 320)).isGreaterThan(0);
    }

    @Test
    void deleteWithValidByIds_allowsSuperAdminToDeleteDeviceOwnedByAnotherUser() {
        AppDeviceServiceImpl service = new AppDeviceServiceImpl(
            appDeviceMapper,
            schedulingDeviceMapper,
            wateringLogMapper,
            wateringLogService
        );
        when(appDeviceMapper.delete(any(Wrapper.class))).thenReturn(1);

        try (MockedStatic<LoginHelper> loginHelper = mockStatic(LoginHelper.class)) {
            loginHelper.when(LoginHelper::isSuperAdmin).thenReturn(true);

            Boolean result = service.deleteWithValidByIds(List.of("D01"), true);

            assertThat(result).isTrue();
            verify(appDeviceMapper, never()).selectCount(any(Wrapper.class));
            verify(appDeviceMapper).delete(any(Wrapper.class));
        }
    }

    @Test
    void deleteWithValidByIds_allowsAuthorizedSystemUserToDeleteDeviceOwnedByAnotherUser() {
        AppDeviceServiceImpl service = new AppDeviceServiceImpl(
            appDeviceMapper,
            schedulingDeviceMapper,
            wateringLogMapper,
            wateringLogService
        );
        when(appDeviceMapper.delete(any(Wrapper.class))).thenReturn(1);

        try (MockedStatic<LoginHelper> loginHelper = mockStatic(LoginHelper.class)) {
            loginHelper.when(LoginHelper::isSuperAdmin).thenReturn(false);
            loginHelper.when(LoginHelper::isTenantAdmin).thenReturn(false);
            loginHelper.when(LoginHelper::getUserType).thenReturn(UserType.SYS_USER);

            Boolean result = service.deleteWithValidByIds(List.of("D01"), true);

            assertThat(result).isTrue();
            verify(appDeviceMapper, never()).selectCount(any(Wrapper.class));
            verify(appDeviceMapper).delete(any(Wrapper.class));
        }
    }

    @Test
    void bindDeviceStatus_findsDeviceByDeviceNo() {
        AppDeviceServiceImpl service = new AppDeviceServiceImpl(
            appDeviceMapper,
            schedulingDeviceMapper,
            wateringLogMapper,
            wateringLogService
        );
        AppDeviceBo bo = new AppDeviceBo();
        bo.setDeviceNo("D01");
        AppDevice device = new AppDevice();
        device.setDeviceNo("D01");
        device.setUserId(100L);
        when(appDeviceMapper.selectById("D01")).thenReturn(device);

        try (MockedStatic<LoginHelper> loginHelper = mockStatic(LoginHelper.class)) {
            loginHelper.when(LoginHelper::getUserId).thenReturn(100L);

            Map<String, Object> result = service.bindDeviceStatus(bo);

            assertThat(result.get("bindDeviceStatus")).isEqualTo(200);
            assertThat(result.get("bindDevice")).isSameAs(device);
            verify(appDeviceMapper).selectById("D01");
            verify(appDeviceMapper, never()).selectByMac(any());
        }
    }

    @Test
    void bindDeviceStatus_findsDeviceByDeviceInitName() {
        AppDeviceServiceImpl service = new AppDeviceServiceImpl(
            appDeviceMapper,
            schedulingDeviceMapper,
            wateringLogMapper,
            wateringLogService
        );
        AppDeviceBo bo = new AppDeviceBo();
        bo.setDeviceInitName("INIT-01");
        AppDevice device = new AppDevice();
        device.setDeviceNo("D01");
        device.setDeviceInitName("INIT-01");
        device.setUserId(100L);
        when(appDeviceMapper.selectByDeviceInitName("INIT-01")).thenReturn(device);

        try (MockedStatic<LoginHelper> loginHelper = mockStatic(LoginHelper.class)) {
            loginHelper.when(LoginHelper::getUserId).thenReturn(100L);

            Map<String, Object> result = service.bindDeviceStatus(bo);

            assertThat(result.get("bindDeviceStatus")).isEqualTo(200);
            assertThat(result.get("bindDevice")).isSameAs(device);
            verify(appDeviceMapper).selectByDeviceInitName("INIT-01");
            verify(appDeviceMapper, never()).selectByMac(any());
        }
    }


    @Test
    void bindDeviceStatus_returnsUnboundWhenNetworkStatusFieldsAreNull() {
        AppDeviceServiceImpl service = new AppDeviceServiceImpl(
            appDeviceMapper,
            schedulingDeviceMapper,
            wateringLogMapper,
            wateringLogService
        );
        AppDeviceBo bo = new AppDeviceBo();
        bo.setDeviceNo("D01");
        AppDevice device = new AppDevice();
        device.setDeviceNo("D01");
        when(appDeviceMapper.selectById("D01")).thenReturn(device);

        try (MockedStatic<LoginHelper> loginHelper = mockStatic(LoginHelper.class)) {
            loginHelper.when(LoginHelper::getUserId).thenReturn(100L);

            Map<String, Object> result = service.bindDeviceStatus(bo);

            assertThat(result.get("bindDeviceStatus")).isEqualTo(304);
            assertThat(result.get("bindDeviceStatusName")).isEqualTo("设备未绑定，请先绑定用户");
        }
    }

    @Test
    void queryByDeviceNos_delegatesToSingleBatchQuery() {
        AppDeviceServiceImpl service = new AppDeviceServiceImpl(
            appDeviceMapper,
            schedulingDeviceMapper,
            wateringLogMapper,
            wateringLogService
        );
        List<String> deviceNos = List.of("D01", "D02");
        List<AppDeviceVo> expected = List.of(new AppDeviceVo(), new AppDeviceVo());
        when(appDeviceMapper.selectVoByIds(deviceNos)).thenReturn(expected);

        assertThat(service.queryByDeviceNos(deviceNos)).isSameAs(expected);
        verify(appDeviceMapper).selectVoByIds(deviceNos);
    }

    private int countNonWhitePixels(BufferedImage image, int startY, int endY) {
        int count = 0;
        for (int y = startY; y < endY; y++) {
            for (int x = 0; x < image.getWidth(); x++) {
                if ((image.getRGB(x, y) & 0x00FFFFFF) != 0x00FFFFFF) {
                    count++;
                }
            }
        }
        return count;
    }
}
