package com.usky.vpp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.usky.common.core.bean.ApiResult;
import com.usky.common.core.bean.CommonPage;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.core.util.UUIDUtils;
import com.usky.common.security.utils.SecurityUtils;
import com.usky.demo.RemoteTsdbProxyService;
import com.usky.demo.domain.DeviceCsvImportResultVO;
import com.usky.iot.RemoteIotTaskService;
import com.usky.vpp.domain.DmpDeviceStatus;
import com.usky.vpp.domain.VppDevice;
import com.usky.vpp.mapper.DmpDeviceStatusMapper;
import com.usky.vpp.mapper.VppDeviceMapper;
import com.usky.vpp.service.VppDeviceService;
import com.usky.vpp.service.VppSiteService;
import com.usky.vpp.service.vo.DeviceRequest;
import com.usky.vpp.service.vo.DeviceTsdbImportResultVO;
import com.usky.vpp.util.VppAuditHelper;
import com.usky.vpp.util.VppPageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class VppDeviceServiceImpl implements VppDeviceService {

    @Autowired
    private VppDeviceMapper deviceMapper;
    @Autowired
    private DmpDeviceStatusMapper dmpDeviceStatusMapper;
    @Autowired
    private VppSiteService siteService;
    @Autowired
    private RemoteIotTaskService remoteIotTaskService;
    @Autowired
    private RemoteTsdbProxyService remoteTsdbProxyService;

    @Override
    public CommonPage<VppDevice> pageDevice(Map<String, Object> params) {
        Page<VppDevice> page = VppPageHelper.of(params);
        return selectDevicePage(page, buildQueryWrapper(params), params);
    }

    @Override
    public CommonPage<VppDevice> listBySiteId(Long siteId, Map<String, Object> params) {
        siteService.getSite(siteId);
        Page<VppDevice> page = VppPageHelper.of(params);
        LambdaQueryWrapper<VppDevice> wrapper = buildQueryWrapper(params)
                .eq(VppDevice::getSiteId, siteId);
        return selectDevicePage(page, wrapper, params);
    }

    private CommonPage<VppDevice> selectDevicePage(Page<VppDevice> page,
                                                   LambdaQueryWrapper<VppDevice> wrapper,
                                                   Map<String, Object> params) {
        Page<VppDevice> result = deviceMapper.selectPage(page, wrapper);
        fillDeviceStatus(result.getRecords(), params);
        return toCommonPage(result);
    }

    @Override
    public VppDevice getDevice(Long id) {
        VppDevice device = deviceMapper.selectById(id);
        if (device == null || VppAuditHelper.isDeleted(device.getDeleteFlag())) {
            throw new BusinessException("设备不存在");
        }
        return device;
    }

    @Override
    public VppDevice getByDeviceUuid(String deviceUuid) {
        if (!StringUtils.hasText(deviceUuid)) {
            throw new BusinessException("设备uuid不能为空");
        }
        VppDevice device = deviceMapper.selectOne(new LambdaQueryWrapper<VppDevice>()
                .eq(VppDevice::getDeviceUuid, deviceUuid)
                .eq(VppDevice::getDeleteFlag, VppAuditHelper.NOT_DELETED)
                .last("LIMIT 1"));
        if (device == null) {
            throw new BusinessException("设备不存在");
        }
        return device;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public VppDevice createDevice(Long siteId, DeviceRequest request) {
        siteService.getSite(siteId);
        validateDeviceRequest(request, null);
        VppDevice device = new VppDevice();
        device.setSiteId(siteId);
        device.setDeviceUuid(UUIDUtils.uuid().substring(0, 16));
        applyRequest(device, request);
        device.setTenantId(SecurityUtils.getTenantId());
        VppAuditHelper.fillCreate(device);
        deviceMapper.insert(device);

        remoteIotTaskService.addDeviceInfo(request.getProductCode(), device.getDeviceUuid(), device.getDeviceCode(),
                device.getDeviceName(), "", 2);
        return device;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDevice(Long id, DeviceRequest request) {
        VppDevice existing = getDevice(id);
        validateDeviceRequest(request, id);
        applyRequest(existing, request);
        VppAuditHelper.fillUpdate(existing);
        deviceMapper.updateById(existing);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteDevice(Long id) {
        VppDevice device = getDevice(id);
        VppAuditHelper.fillSoftDelete(device);
        deviceMapper.updateById(device);

        remoteIotTaskService.deleteDeviceInfo(device.getDeviceUuid());
    }

    @Override
    public DeviceTsdbImportResultVO importTsdbData(String deviceUuid, MultipartFile file) {
        getByDeviceUuid(deviceUuid);
        if (file == null || file.isEmpty()) {
            throw new BusinessException("导入文件不能为空");
        }
        ApiResult<DeviceCsvImportResultVO> result = remoteTsdbProxyService.importDeviceCsv(deviceUuid, file);
        if (result == null || !result.isSuccess() || result.getData() == null) {
            throw new BusinessException(result != null && StringUtils.hasText(result.getMsg())
                    ? result.getMsg() : "时序数据导入失败");
        }
        DeviceCsvImportResultVO data = result.getData();
        DeviceTsdbImportResultVO vo = new DeviceTsdbImportResultVO();
        vo.setDeviceUuid(data.getDeviceUuid());
        vo.setTableName(data.getTableName());
        vo.setImportedRows(data.getImportedRows());
        return vo;
    }

    private LambdaQueryWrapper<VppDevice> buildQueryWrapper(Map<String, Object> params) {
        LambdaQueryWrapper<VppDevice> wrapper = new LambdaQueryWrapper<VppDevice>()
                .eq(VppDevice::getDeleteFlag, VppAuditHelper.NOT_DELETED)
                .eq(VppDevice::getTenantId, SecurityUtils.getTenantId())
                .orderByDesc(VppDevice::getCreateTime);
        if (params == null) {
            return wrapper;
        }
        if (params.get("siteId") != null) {
            wrapper.eq(VppDevice::getSiteId, Long.parseLong(params.get("siteId").toString()));
        }
        if (params.get("deviceCode") != null) {
            wrapper.eq(VppDevice::getDeviceCode, params.get("deviceCode").toString());
        }
        if (params.get("deviceUuid") != null) {
            wrapper.eq(VppDevice::getDeviceUuid, params.get("deviceUuid").toString());
        }
        if (params.get("deviceName") != null) {
            wrapper.like(VppDevice::getDeviceName, params.get("deviceName").toString());
        }
        if (params.get("deviceType") != null) {
            wrapper.eq(VppDevice::getDeviceType, params.get("deviceType").toString());
        }
        if (params.get("unResourceId") != null && StringUtils.hasText(params.get("unResourceId").toString())) {
            wrapper.eq(VppDevice::getUnResourceId, params.get("unResourceId").toString().trim());
        }
        applyRunStatusFilter(wrapper, params);
        return wrapper;
    }

    private void applyRunStatusFilter(LambdaQueryWrapper<VppDevice> wrapper, Map<String, Object> params) {
        Integer runStatus = resolveRunStatus(params);
        if (runStatus == null) {
            return;
        }
        wrapper.isNotNull(VppDevice::getDeviceUuid)
                .ne(VppDevice::getDeviceUuid, "")
                .apply("EXISTS (SELECT 1 FROM dmp_device_status ds WHERE ds.device_uuid = vpp_device.device_uuid AND ds.device_status = {0})", runStatus);
    }

    private Integer resolveRunStatus(Map<String, Object> params) {
        if (params == null || params.get("runStatus") == null) {
            return null;
        }
        return Integer.parseInt(params.get("runStatus").toString());
    }

    private void fillDeviceStatus(List<VppDevice> devices, Map<String, Object> params) {
        if (CollectionUtils.isEmpty(devices)) {
            return;
        }
        Integer runStatus = resolveRunStatus(params);
        if (runStatus != null) {
            devices.forEach(device -> device.setDeviceStatus(runStatus));
            return;
        }
        List<String> deviceUuids = devices.stream()
                .map(VppDevice::getDeviceUuid)
                .filter(StringUtils::hasText)
                .distinct()
                .collect(Collectors.toList());
        if (deviceUuids.isEmpty()) {
            return;
        }
        Map<String, Integer> statusByUuid = dmpDeviceStatusMapper.selectList(
                        new LambdaQueryWrapper<DmpDeviceStatus>()
                                .select(DmpDeviceStatus::getDeviceUuid, DmpDeviceStatus::getDeviceStatus)
                                .in(DmpDeviceStatus::getDeviceUuid, deviceUuids))
                .stream()
                .filter(status -> StringUtils.hasText(status.getDeviceUuid()))
                .collect(Collectors.toMap(DmpDeviceStatus::getDeviceUuid, DmpDeviceStatus::getDeviceStatus, (a, b) -> a));
        devices.forEach(device -> device.setDeviceStatus(statusByUuid.get(device.getDeviceUuid())));
    }

    private void validateDeviceRequest(DeviceRequest request, Long excludeId) {
        if (request == null || !StringUtils.hasText(request.getDeviceCode())) {
            throw new BusinessException("设备编号不能为空");
        }
        if (!StringUtils.hasText(request.getDeviceName())) {
            throw new BusinessException("设备名称不能为空");
        }
        ensureUniqueField(VppDevice::getDeviceCode, request.getDeviceCode(), excludeId, "设备编号已存在");
        if (StringUtils.hasText(request.getUnResourceId())) {
            ensureUniqueField(VppDevice::getUnResourceId, request.getUnResourceId().trim(), excludeId, "运管资源ID已存在");
        }
    }

    private void ensureUniqueField(com.baomidou.mybatisplus.core.toolkit.support.SFunction<VppDevice, ?> field,
                                   String value, Long excludeId, String message) {
        LambdaQueryWrapper<VppDevice> wrapper = new LambdaQueryWrapper<VppDevice>()
                .eq(field, value)
                .eq(VppDevice::getDeleteFlag, VppAuditHelper.NOT_DELETED);
        if (excludeId != null) {
            wrapper.ne(VppDevice::getId, excludeId);
        }
        if (deviceMapper.selectCount(wrapper) > 0) {
            throw new BusinessException(message);
        }
    }

    private void applyRequest(VppDevice device, DeviceRequest request) {
        device.setDeviceCode(request.getDeviceCode());
        device.setDeviceName(request.getDeviceName());
        device.setDeviceType(request.getDeviceType());
        device.setManufacturer(request.getManufacturer());
        device.setModel(request.getModel());
        device.setRatedPowerKw(request.getRatedPowerKw());
        device.setFirmwareVersion(request.getFirmwareVersion());
        device.setGatewayId(request.getGatewayId());
        device.setUnResourceId(StringUtils.hasText(request.getUnResourceId()) ? request.getUnResourceId().trim() : null);
        device.setRemark(request.getRemark());
    }

    private CommonPage<VppDevice> toCommonPage(Page<VppDevice> page) {
        return new CommonPage<>(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize());
    }
}
