package com.multictrl.modules.business.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.multictrl.common.config.MinioConfig;
import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.exception.RenException;
import com.multictrl.common.service.impl.CrudServiceImpl;
import com.multictrl.common.utils.ConvertUtils;
import com.multictrl.common.utils.DateUtils;
import com.multictrl.modules.business.dao.DockDeviceDao;
import com.multictrl.modules.business.dao.FirmwareDao;
import com.multictrl.modules.business.dto.DockDeviceDTO;
import com.multictrl.modules.business.dto.FirmwareDTO;
import com.multictrl.modules.business.dto.firmware.FirmwareVersion;
import com.multictrl.modules.business.entity.DockDeviceEntity;
import com.multictrl.modules.business.entity.FirmwareEntity;
import cn.hutool.core.util.StrUtil;
import com.multictrl.modules.business.service.DJIBaseService;
import com.multictrl.modules.business.service.FirmwareService;
import com.multictrl.modules.business.service.MinioService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * 固件表
 *
 * @author Sdy
 * @since 1.0.0 2026-05-11
 */
@Service
@RequiredArgsConstructor
public class FirmwareServiceImpl extends CrudServiceImpl<FirmwareDao, FirmwareEntity, FirmwareDTO> implements FirmwareService {
    private final MinioService minioService;
    private final DockDeviceDao dockDeviceDao;
    private final DJIBaseService djiBaseService;
    private final MinioConfig minioConfig;
    public static final String FIRMWARE_FILE_SUFFIX = ".zip";
    public static final String FIRMWARE_SIG_FILE_SUFFIX = ".sig";
    public static final String FIRMWARE_CONFIG_FILE_SUFFIX = ".cfg";
    public static final String FIRMWARE_FILE_DELIMITER = "_";
    public static final int FILENAME_VERSION_INDEX = 2;
    public static final int FILENAME_RELEASE_DATE_INDEX = 3;
    public static final String FILENAME_RELEASE_DATE_FORMAT = "yyyyMMdd";

    @Override
    public QueryWrapper<FirmwareEntity> getWrapper(Map<String, Object> params) {
        String id = (String) params.get("id");

        QueryWrapper<FirmwareEntity> wrapper = new QueryWrapper<>();
        wrapper.eq(StrUtil.isNotBlank(id), "id", id);
        wrapper.orderByDesc("create_date");

        return wrapper;
    }

    @Override
    public List<FirmwareDTO> getFirmwareList(String dockSn) {
        List<DockDeviceEntity> list = dockDeviceDao.selectList(new QueryWrapper<DockDeviceEntity>().eq("sn", dockSn).or().eq("parent_sn", dockSn));
        if (CollectionUtil.isNotEmpty(list)) {
            List<FirmwareEntity> firmwareList = baseDao.selectList(new QueryWrapper<FirmwareEntity>()
                    .in("device_name", list.stream().map(DockDeviceEntity::getDeviceName).collect(Collectors.toList()))
                    .orderByDesc("released_date"));

            return ConvertUtils.sourceToTarget(firmwareList, FirmwareDTO.class);
        }

        return List.of();
    }

    @Override
    public void uploadFirmware(MultipartFile file, String deviceName, String remark) {
        if (file == null || file.getSize() < 10) {
            throw new RenException(ErrorCode.UPLOAD_FILE_EMPTY);
        }
        String originalFilename = file.getOriginalFilename();
        FirmwareEntity entity = new FirmwareEntity();
        verifyFirmwareFile(file, entity);
        if (entity.getReleasedDate() == null || entity.getFirmwareVersion() == null) {
            throw new RenException(ErrorCode.FIRMWARE_NOT_OFFICIAL);
        }
        Long count = baseDao.selectCount(new QueryWrapper<FirmwareEntity>().eq("device_name", deviceName)
                .eq("firmware_version", entity.getFirmwareVersion()));
        if (count > 0) {
            throw new RenException(ErrorCode.DEVICE_FIRMWARE_EXIST);
        }
        entity.setFileName(file.getOriginalFilename());
        entity.setDeviceName(deviceName);
        entity.setRemark(remark);
        try (
                InputStream is = file.getInputStream();
                InputStream is2 = file.getInputStream()
        ) {
            entity.setFileSize((long) is.available());
            entity.setFileMd5(DigestUtils.md5DigestAsHex(is));
            String path = minioService.uploadFirmware(is2, deviceName, originalFilename);
            entity.setPath(path);
        } catch (Exception i) {
            throw new RenException(ErrorCode.OSS_UPLOAD_FILE_ERROR);
        }
        baseDao.insert(entity);
    }

    @Override
    public void deleteFirmware(Long[] ids) {
        for (Long id : ids) {
            FirmwareEntity entity = selectById(id);
            String path = entity.getPath();
            minioService.removeFile(BusinessConstant.DEVICE_FIRMWARE_BUCKET, path.replaceAll(BusinessConstant.DEVICE_FIRMWARE_BUCKET, ""));
        }
        delete(ids);
    }

    @Override
    public String ota(String dockSn, Long firmwareId) {
        FirmwareEntity entity = selectById(firmwareId);
        if (entity == null) {
            throw new RenException(ErrorCode.FIRMWARE_NOT_EXIST);
        }
        DockDeviceEntity dockDevice = dockDeviceDao.selectOne(new QueryWrapper<DockDeviceEntity>().eq("sn", dockSn));
        if (dockDevice == null) {
            throw new RenException(ErrorCode.DOCK_NOT_REGISTER);
        }
        JSONObject data = new JSONObject();
        JSONArray devices = new JSONArray();
        JSONObject device = new JSONObject();
        device.set("file_name", entity.getFileName());
        device.set("file_size", entity.getFileSize());
        device.set("file_url", minioConfig.getEndpoint() + "/" + entity.getPath());
        device.set("firmware_upgrade_type", 3);
        device.set("md5", entity.getFileMd5());
        device.set("product_version", entity.getFirmwareVersion());
        if (entity.getDeviceName().equals(dockDevice.getDeviceName())) {
            device.set("sn", dockSn);
        } else {
            DockDeviceEntity uavDevice = dockDeviceDao.selectOne(new QueryWrapper<DockDeviceEntity>().eq("parent_sn", dockSn));
            if (uavDevice == null) {
                throw new RenException(ErrorCode.UAV_NOT_REGISTER);
            }
            device.set("sn", uavDevice.getSn());
        }
        devices.add(device);
        data.set("devices", devices);
        return djiBaseService.executeAndReturnResult(dockSn, "ota_create", data);
    }

    @Override
    public List<DockDeviceDTO> registerDevices() {
        List<DockDeviceEntity> list = dockDeviceDao.selectList(new QueryWrapper<DockDeviceEntity>().in("domain", 0, 3)
                .orderByDesc("domain"));

        return ConvertUtils.sourceToTarget(list, DockDeviceDTO.class);
    }

    @Override
    public FirmwareVersion getCurrentFirmwareVersion(String dockSn) {
        DockDeviceEntity dockDevice = dockDeviceDao.selectOne(new QueryWrapper<DockDeviceEntity>().eq("sn", dockSn));
        if (dockDevice == null) {
            throw new RenException(ErrorCode.DOCK_NOT_REGISTER);
        }
        DockDeviceEntity uavDevice = dockDeviceDao.selectOne(new QueryWrapper<DockDeviceEntity>().eq("parent_sn", dockSn));
        if (uavDevice == null) {
            throw new RenException(ErrorCode.UAV_NOT_REGISTER);
        }
        FirmwareVersion firmwareVersion = new FirmwareVersion();
        firmwareVersion.setDockFirmware(dockDevice.getThingVersion());
        firmwareVersion.setUavFirmware(uavDevice.getThingVersion());
        return firmwareVersion;
    }

    private void verifyFirmwareFile(MultipartFile file, FirmwareEntity firmwareEntity) {
        try (ZipInputStream unzipFile = new ZipInputStream(file.getInputStream(),
                StandardCharsets.UTF_8)) {
            ZipEntry nextEntry = unzipFile.getNextEntry();
            while (Objects.nonNull(nextEntry)) {
                String configName = nextEntry.getName();
                if (!configName.contains(File.separator) && configName.endsWith(FIRMWARE_CONFIG_FILE_SUFFIX + FIRMWARE_SIG_FILE_SUFFIX)) {
                    String[] filenameArr = configName.split(FIRMWARE_FILE_DELIMITER);
                    String date = filenameArr[FILENAME_RELEASE_DATE_INDEX];
                    int index = date.indexOf(".");
                    if (index != -1) {
                        date = date.substring(0, index);
                    }
                    firmwareEntity.setReleasedDate(DateUtils.parse(date, FILENAME_RELEASE_DATE_FORMAT));
                    firmwareEntity.setFirmwareVersion(filenameArr[FILENAME_VERSION_INDEX].substring(1));
                    break;
                }
                nextEntry = unzipFile.getNextEntry();
            }

        } catch (IOException e) {
            log.error("error", e);
        }
    }
}