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

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
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.page.PageData;
import com.multictrl.common.service.impl.CrudServiceImpl;
import cn.hutool.core.util.StrUtil;
import com.multictrl.common.utils.ConvertUtils;
import com.multictrl.common.utils.DateUtils;
import com.multictrl.modules.business.dao.MediaFileDao;
import com.multictrl.modules.business.dto.MediaFileDTO;
import com.multictrl.modules.business.entity.MediaFileEntity;
import com.multictrl.modules.business.service.MediaFileService;
import com.multictrl.modules.security.user.SecurityUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 媒体资源
 *
 * @author Sdy
 * @since 1.0.0 2026-04-29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MediaFileServiceImpl extends CrudServiceImpl<MediaFileDao, MediaFileEntity, MediaFileDTO> implements MediaFileService {
    private final MinioConfig minioConfig;

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

        QueryWrapper<MediaFileEntity> wrapper = new QueryWrapper<>();
        wrapper.eq(StrUtil.isNotBlank(id), "id", id);
        wrapper.eq(StrUtil.isNotBlank(taskId), "task_id", taskId);
        wrapper.orderByDesc("created_time");

        return wrapper;
    }

    @Override
    public PageData<JSONObject> pageList(Map<String, Object> params) {
        IPage<MediaFileEntity> iPage = baseDao.pageList(getPage(params, null, false), getWrapperBy(params));
        PageData<MediaFileDTO> page = getPageData(iPage, MediaFileDTO.class);
        List<JSONObject> newList = new ArrayList<>();
        List<MediaFileDTO> list = page.getList();
        if (CollectionUtil.isNotEmpty(list)) {
            Map<String, List<MediaFileDTO>> collect = list.stream()
                    .filter(v -> v != null && v.getCreatedTime() != null)
                    .peek(v -> {
                        String objectKey = v.getObjectKey();
                        String subFileType = v.getSubFileType();
                        if ("video".equals(subFileType)) {
                            v.setUrl(BusinessConstant.VIDEO_PATH + BusinessConstant.DOCK_MEDIA_BUCKET + "/" + objectKey);
                            v.setCover(BusinessConstant.IMAGE_PATH + BusinessConstant.DOCK_MEDIA_BUCKET + "/" +
                                    objectKey.replaceAll(".mp4", BusinessConstant.VIDEO_COVER_SUFFIX));
                        } else {
                            v.setUrl(BusinessConstant.IMAGE_PATH + BusinessConstant.DOCK_MEDIA_BUCKET + "/" + objectKey);
                        }
                    }).collect(Collectors.groupingBy(
                            v -> DateUtils.format(v.getCreatedTime(), "yyyy年MM月dd日"),
                            () -> new TreeMap<String, List<MediaFileDTO>>(Comparator.reverseOrder()),
                            Collectors.toList()
                    ));
            for (String key : collect.keySet()) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.set("date", key);
                jsonObject.set("list", collect.get(key));
                newList.add(jsonObject);
            }
        }
        return new PageData<>(newList, page.getTotal());
    }

    @Override
    public PageData<MediaFileDTO> getAlbum(Map<String, Object> params) {
        String dockSn = (String) params.get("dockSn");
        IPage<MediaFileEntity> pageList = this.baseDao.getAlbum(getPage(params, null, false), SecurityUser.getDeptId(), dockSn);
        PageData<MediaFileDTO> pageData = getPageData(pageList, MediaFileDTO.class);
        for (MediaFileDTO dto : pageData.getList()) {
            String objectKey = dto.getObjectKey();
            dto.setUrl(BusinessConstant.IMAGE_PATH + BusinessConstant.DOCK_MEDIA_BUCKET + "/" + objectKey);
        }
        return pageData;
    }

    @Override
    public void deleteMediaFile(Long[] ids) {
        for (Long id : ids) {
            MediaFileDTO dto = get(id);
            if (dto != null) {
                String objectKey = dto.getObjectKey();
                FileUtil.del(minioConfig.getOther().getDataPath() + BusinessConstant.DOCK_MEDIA_BUCKET + "/" + objectKey);
                deleteById(id);
            }
        }
    }

    @Override
    public ResponseEntity<InputStreamResource> downloadMedia(Long id) {
        try {
            MediaFileDTO dto = get(id);
            String filePath = minioConfig.getOther().getDataPath() + BusinessConstant.DOCK_MEDIA_BUCKET + "/" + dto.getObjectKey();
            String fileName = dto.getName();
            File file = new File(filePath);

            // 检查文件是否存在
            if (!file.exists()) {
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
            }

            // 设置 Content-Disposition 头
            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replace("+", "%20");
            String contentDisposition = "attachment; filename=" + encodedFileName;

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_OCTET_STREAM_VALUE));
            headers.set(HttpHeaders.CONTENT_DISPOSITION, contentDisposition);
            headers.setContentLength((int) Files.size(file.toPath()));

            // 使用 InputStreamResource 支持大文件流式传输
            InputStream inputStream = Files.newInputStream(file.toPath());
            return new ResponseEntity<>(new InputStreamResource(inputStream), headers, HttpStatus.OK);

        } catch (IOException e) {
            log.error("媒体文件下载失败：{}", ExceptionUtils.getStackTrace(e));
            throw new RenException(ErrorCode.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    public ResponseEntity<InputStreamResource> downloadMediaByZip(Map<String, Object> params) {
        ZipOutputStream zipOut = null;
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            zipOut = new ZipOutputStream(baos);

            // 遍历所有ID，将文件添加到ZIP中
            List<MediaFileEntity> list = baseDao.list(getWrapperBy(params));
            for (MediaFileEntity entity : list) {
                try {
                    String filePath = minioConfig.getOther().getDataPath() + BusinessConstant.DOCK_MEDIA_BUCKET + "/" + entity.getObjectKey();
                    String fileName = entity.getName();
                    File file = new File(filePath);

                    // 创建ZIP条目
                    ZipEntry zipEntry = new ZipEntry(fileName);
                    zipOut.putNextEntry(zipEntry);

                    // 将文件内容写入ZIP
                    Files.copy(file.toPath(), zipOut);

                    zipOut.closeEntry();

                } catch (Exception e) {
                    // 记录错误但继续处理其他文件
                    log.error("处理文件ID {} 时出错: {}", entity.getId(), e.getMessage());
                }
            }

            // 关闭ZIP流
            zipOut.close();
            baos.close();

            // 设置响应头
            String zipFileName = "media_" + System.currentTimeMillis() + ".zip";
            String encodedFileName = URLEncoder.encode(zipFileName, StandardCharsets.UTF_8).replace("+", "%20");
            String contentDisposition = "attachment; filename=" + encodedFileName;

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.parseMediaType("application/zip"));
            headers.set(HttpHeaders.CONTENT_DISPOSITION, contentDisposition);
            headers.setContentLength(baos.size());

            // 创建输入流资源
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            return new ResponseEntity<>(new InputStreamResource(bais), headers, HttpStatus.OK);

        } catch (IOException e) {
            // 关闭流
            try {
                zipOut.close();
                baos.close();
            } catch (IOException ex) {
                log.error("媒体文件(zip)下载失败：{}", ExceptionUtils.getStackTrace(e));
            }
            throw new RenException(ErrorCode.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    public Map<String, List<String>> oneClickGetDownloadPath(Map<String, Object> params) {
        Map<String, List<String>> map = new HashMap<>();
        List<String> images = new ArrayList<>();
        List<String> videos = new ArrayList<>();
        List<MediaFileEntity> entityList = baseDao.list(getWrapperBy(params));
        List<MediaFileDTO> dtos = ConvertUtils.sourceToTarget(entityList, MediaFileDTO.class);
        for (MediaFileDTO dto : dtos) {
            String name = dto.getName();
            if (StrUtil.containsAnyIgnoreCase(name, "mp4")) {
                String url = BusinessConstant.VIDEO_PATH + BusinessConstant.DOCK_MEDIA_BUCKET + "/" + dto.getObjectKey();
                videos.add(url);
            } else {
                String url = BusinessConstant.IMAGE_PATH + BusinessConstant.DOCK_MEDIA_BUCKET + "/" + dto.getObjectKey();
                images.add(url);
            }
        }
        map.put("images", images);
        map.put("videos", videos);
        return map;
    }

    @Override
    public void conditionDeleteMedia(Map<String, Object> params) {
        List<MediaFileEntity> list = baseDao.list(getWrapperBy(params));
        if (CollectionUtil.isEmpty(list)) {
            return;
        }
        for (MediaFileEntity entity : list) {
            String path = minioConfig.getOther().getDataPath() + BusinessConstant.DOCK_MEDIA_BUCKET + "/" + entity.getObjectKey();
            FileUtil.del(path);
            deleteById(entity.getId());
        }
    }

    //获取条件
    private QueryWrapper<MediaFileEntity> getWrapperBy(Map<String, Object> params) {
        String dockSn = (String) params.get("dockSn");
        String routeId = (String) params.get("routeId");
        String startTime = (String) params.get("startTime");
        String endTime = (String) params.get("endTime");
        QueryWrapper<MediaFileEntity> wrapper = new QueryWrapper<>();
        wrapper.eq(StrUtil.isNotBlank(dockSn), "t.dock_sn", dockSn);
        if (StrUtil.isNotBlank(routeId)) {
            wrapper.eq("t.route_id", Long.parseLong(routeId));
        }
        if (StrUtil.isNotBlank(startTime) && StrUtil.isNotBlank(endTime)) {
            wrapper.between("m.created_time", DateUtils.parse(startTime, DateUtils.DATE_TIME_PATTERN), DateUtils.parse(endTime, DateUtils.DATE_TIME_PATTERN));
        }
        wrapper.orderByDesc("m.created_time"/*, "t.create_date"*/);
//        wrapper.orderByAsc("m.off_index");

        return wrapper;
    }

    @Override
    public MediaFileDao getDao() {
        return baseDao;
    }
}