package com.dji.sample.media.service.impl;

import com.dji.sample.component.oss.model.OssConfiguration;
import com.dji.sample.component.redis.RedisOpsUtils;
import com.dji.sample.component.websocket.model.BizCodeEnum;
import com.dji.sample.component.websocket.service.IWebSocketMessageService;
import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.dji.sample.manage.service.IDeviceRedisService;
import com.dji.sample.manage.service.IDeviceService;
import com.dji.sample.media.model.MediaFileCountDTO;
import com.dji.sample.media.model.MediaFileDTO;
import com.dji.sample.media.service.IFileService;
import com.dji.sample.media.service.IMediaRedisService;
import com.dji.sample.media.service.IMediaService;
import com.dji.sample.wayline.model.dto.WaylineJobDTO;
import com.dji.sample.wayline.service.IWaylineJobService;
import com.dji.sdk.cloudapi.control.TakeoffToPointRequest;
import com.dji.sdk.cloudapi.media.*;
import com.dji.sdk.cloudapi.media.api.AbstractMediaService;
import com.dji.sdk.mqtt.MqttReply;
import com.dji.sdk.mqtt.events.TopicEventsRequest;
import com.dji.sdk.mqtt.events.TopicEventsResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.MessageHeaders;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author sean
 * @version 0.2
 * @date 2021/12/9
 */
@Service
@Slf4j
public class MediaServiceImpl extends AbstractMediaService implements IMediaService {

    @Autowired
    private IFileService fileService;

    @Autowired
    private IWaylineJobService waylineJobService;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private IDeviceService deviceService;

    @Autowired
    private IWebSocketMessageService webSocketMessageService;

    @Autowired
    private IDeviceRedisService deviceRedisService;

    @Autowired
    private IMediaRedisService mediaRedisService;

    @Value("${my.file.base-path}")
    private String basePath;

    @Override
    public Boolean fastUpload(String workspaceId, String fingerprint) {
        return fileService.checkExist(workspaceId, fingerprint);
    }

    @Override
    public Integer saveMediaFile(String workspaceId, MediaUploadCallbackRequest file) {
        return fileService.saveFile(workspaceId, file);
    }

    @Override
    public List<String> getAllTinyFingerprintsByWorkspaceId(String workspaceId) {
        return fileService.getAllFilesByWorkspaceId(workspaceId)
                .stream()
                .map(MediaFileDTO::getTinnyFingerprint)
                .collect(Collectors.toList());
    }

    @Override
    public List<String> getExistTinyFingerprints(String workspaceId, List<String> tinyFingerprints) {
        List<String> tinyFingerprintList = this.getAllTinyFingerprintsByWorkspaceId(workspaceId);
        return tinyFingerprints
                .stream()
                .filter(tinyFingerprintList::contains)
                .collect(Collectors.toList());

    }

    @Override
    public TopicEventsResponse<MqttReply> fileUploadCallback(TopicEventsRequest<FileUploadCallback> request, MessageHeaders headers) {
        FileUploadCallback callback = request.getData();

        if (MqttReply.CODE_SUCCESS != callback.getResult()) {
            log.error("Media file upload failed!");
            return null;
        }

        String jobId = callback.getFile().getExt().getFlightId();

        Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(request.getGateway());
        MediaFileCountDTO mediaFileCount = mediaRedisService.getMediaCount(request.getGateway(), jobId);
        // duplicate data
        if (deviceOpt.isEmpty()
                || (Objects.nonNull(mediaFileCount) && request.getBid().equals(mediaFileCount.getBid())
                && request.getTid().equals(mediaFileCount.getTid()))) {
            return new TopicEventsResponse<MqttReply>().setData(MqttReply.success());
        }

        DeviceDTO device = deviceOpt.get();
        boolean isSave = parseMediaFile(callback, device);
        if (!isSave) {
            log.error("Failed to save the file to the database, please check the data manually.");
            return null;
        }

        notifyUploadedCount(mediaFileCount, request, jobId, device);
        return new TopicEventsResponse<MqttReply>().setData(MqttReply.success());
    }

    @Override
    public TopicEventsResponse<MqttReply> highestPriorityUploadFlightTaskMedia(
            TopicEventsRequest<HighestPriorityUploadFlightTaskMedia> request, MessageHeaders headers) {
        String jobId = request.getData().getFlightId();
        if (!StringUtils.hasText(jobId)) {
            return null;
        }

        MediaFileCountDTO countDTO = mediaRedisService.getMediaHighestPriority(request.getGateway());
        if (Objects.nonNull(countDTO)) {
            if (jobId.equals(countDTO.getJobId())) {
                mediaRedisService.setMediaHighestPriority(request.getGateway(), countDTO);
                return new TopicEventsResponse<MqttReply>().setData(MqttReply.success());
            }
            countDTO.setPreJobId(countDTO.getJobId());
        }
        countDTO.setJobId(jobId);
        mediaRedisService.setMediaHighestPriority(request.getGateway(), countDTO);

        Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(request.getGateway());
        if (deviceOpt.isEmpty()) {
            return null;
        }

        webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(),
                BizCodeEnum.HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA.getCode(), countDTO);

        return new TopicEventsResponse<MqttReply>().setData(MqttReply.success());
    }

    private Boolean parseMediaFile(FileUploadCallback callback, DeviceDTO device) {

        // moveFile path wayline/workspaceId/jobId/fileName   ->   wayline/worksapceId/orgId/jobId/fileName
        // file.path name objectKey

        MediaUploadCallbackRequest file = convert2callbackRequest(callback.getFile());
        // Set the drone sn that shoots the media
        file.getExt().setSn(device.getChildDeviceSn());
        String objectKey = file.getObjectKey();

        String path = objectKey.substring(Optional.of(objectKey.indexOf(OssConfiguration.objectDirPrefix))
                .filter(index -> index > 0).map(index -> index++).orElse(0), objectKey.lastIndexOf("/"));
        String orgFilePath = path;

        //可能是临时任务
        if (RedisOpsUtils.checkExist("templateTask:"+callback.getFile().getExt().getFlightId())) {
            TakeoffToPointRequest takeoffToPointRequest = (TakeoffToPointRequest) RedisOpsUtils.get("templateTask:" + callback.getFile().getExt().getFlightId());

            String orgId = takeoffToPointRequest.getOrgId();

            String prePath = OssConfiguration.objectDirPrefix + "/" + device.getWorkspaceId() + "/" + orgId + "/" + takeoffToPointRequest.getDockNickName() + "/临时任务";

            if (path.contains(OssConfiguration.objectDirPrefix + "/" + device.getWorkspaceId())) {
                path = path.replace(OssConfiguration.objectDirPrefix + "/" + device.getWorkspaceId(), prePath);
            } else {
                path = path.replace(OssConfiguration.objectDirPrefix, prePath);
            }
            // 过滤变更名字
            path = path.replace(callback.getFile().getExt().getFlightId(), takeoffToPointRequest.getFlightName());

            String moveFilePath = path;
            file.setPath(path);
            file.setObjectKey(moveFilePath + "/" + callback.getFile().getName());

            try {
                moveFile(orgFilePath, moveFilePath, callback.getFile().getName());
            } catch (Exception e) {
                log.error("Failed to move file for temporary task: {}", e.getMessage());
                // 继续执行，不中断流程
            }
        } else {
            // 查询 任务的orgId
            String orgId = "";
            WaylineJobDTO jobDTO = null;
            Optional<WaylineJobDTO> jobOpt = waylineJobService.getJobByJobId(device.getWorkspaceId(), callback.getFile().getExt().getFlightId());
            if (jobOpt.isPresent()) {
                jobDTO = jobOpt.get();
                orgId = jobDTO.getOrgId();
            }
            // set path
            // todo? 是否需要修改？
            if (StringUtils.hasText(orgId)) {

                String prePath = OssConfiguration.objectDirPrefix + "/" + device.getWorkspaceId() + "/" + orgId + "/" + device.getNickname() + "/" + "航线任务";
                // 增加 org路径
                if (path.contains(OssConfiguration.objectDirPrefix + "/" + device.getWorkspaceId())) {
                    path = path.replace(OssConfiguration.objectDirPrefix + "/" + device.getWorkspaceId(), prePath);
                } else {
                    path = path.replace(OssConfiguration.objectDirPrefix, prePath);
                }
                // jobId改成 jobName
//                if (jobDTO != null) {
//                    path = path.replace(callback.getFile().getExt().getFlightId(), jobDTO.getJobName());
//                }
                String moveFilePath = path;
                // DJI_20250801115516_0033_V.jpeg
                // wayline/e3dea0f5-37f2-4d79-ae58-490af3228069/test-8-1-11-52-2025-08-01-11-52-49-9c08/DJI_202508011152_001_test-8-1-11-52-2025-08-01-11-52-49-9c08/DJI_20250801115516_0033_V.jpeg
                file.setPath(path);
                file.setObjectKey(moveFilePath + "/" + callback.getFile().getName());

                try {
                    moveFile(orgFilePath, moveFilePath, callback.getFile().getName());
                } catch (Exception e) {
                    log.error("Failed to move file for wayline job: {}", e.getMessage());
                    // 继续执行，不中断流程
                }
            } else {
                file.setPath(path);
            }
        }

        return fileService.saveFile(device.getWorkspaceId(), file) > 0;
    }

    @SneakyThrows
    public void moveFile(String sourceFilePath, String targetFilePath, String fileName) {
        File directory = new File(basePath);
        if (!directory.exists()) {
            boolean created = directory.mkdirs();
        }
        Path source = Paths.get(basePath, OssConfiguration.bucket, sourceFilePath, fileName);
        // 检查源文件是否存在
        if (!Files.exists(source)) {
            log.warn("Source file does not exist: {}", source);
            return; // 源文件不存在时直接返回，不抛出异常
        }
        Path targetDir = Paths.get(basePath, OssConfiguration.bucket, targetFilePath);
        if (!Files.exists(targetDir)) {
            Files.createDirectories(targetDir);
        }
        Path target = targetDir.resolve(fileName);
        Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
    }

    private void notifyUploadedCount(MediaFileCountDTO mediaFileCount, TopicEventsRequest<FileUploadCallback> request, String jobId, DeviceDTO dock) {
        // Do not notify when files that do not belong to the route are uploaded.
        if (Objects.isNull(mediaFileCount)) {
            return;
        }
        mediaFileCount.setBid(request.getBid());
        mediaFileCount.setTid(request.getTid());
        mediaFileCount.setUploadedCount(mediaFileCount.getUploadedCount() + 1);

        // After all the files of the job are uploaded, delete the media file key.
        if (mediaFileCount.getUploadedCount() >= mediaFileCount.getMediaCount()) {
            mediaRedisService.delMediaCount(request.getGateway(), jobId);

            // After uploading, delete the key with the highest priority.
            MediaFileCountDTO fileCount = mediaRedisService.getMediaHighestPriority(request.getGateway());
            if (Objects.nonNull(fileCount) && jobId.equals(fileCount.getJobId())) {
                mediaRedisService.delMediaHighestPriority(request.getGateway());
            }
        } else {
            mediaRedisService.setMediaCount(request.getGateway(), jobId, mediaFileCount);
        }

        webSocketMessageService.sendBatch(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal(),
                BizCodeEnum.FILE_UPLOAD_CALLBACK.getCode(), mediaFileCount);
    }

    private MediaUploadCallbackRequest convert2callbackRequest(FileUploadCallbackFile file) {
        if (Objects.isNull(file)) {
            return null;
        }
        return new MediaUploadCallbackRequest()
                .setExt(Optional.ofNullable(file.getExt())
                        .map(ext -> new MediaFileExtension()
                                .setDroneModelKey(ext.getDroneModelKey())
                                .setFileGroupId(ext.getFlightId())
                                .setOriginal(ext.getOriginal())
                                .setPayloadModelKey(ext.getPayloadModelKey()))
                        .orElse(new MediaFileExtension()))
                .setMetadata(Optional.ofNullable(file.getMetadata())
                        .map(data -> new MediaFileMetadata()
                                .setAbsoluteAltitude(data.getAbsoluteAltitude())
                                .setGimbalYawDegree(data.getGimbalYawDegree())
                                .setRelativeAltitude(data.getRelativeAltitude())
                                .setShootPosition(data.getShootPosition())
                                .setCreatedTime(data.getCreatedTime()))
                        .get())
                .setName(file.getName())
                .setObjectKey(file.getObjectKey())
                .setPath(file.getPath());
    }
}
