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

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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 com.multictrl.common.utils.ConvertUtils;
import com.multictrl.common.utils.Utils;
import com.multictrl.common.utils.kmz.KmzUtils;
import com.multictrl.modules.business.dao.GeoPhotoDao;
import com.multictrl.modules.business.dto.GeoPhotoDTO;
import com.multictrl.modules.business.entity.GeoPhotoEntity;
import com.multictrl.modules.business.service.GeoPhotoService;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

/**
 * 地理照片模型
 *
 * @author Sdy
 * @since 1.0.0 2026-06-05
 */
@Slf4j
@Service
public class GeoPhotoServiceImpl extends CrudServiceImpl<GeoPhotoDao, GeoPhotoEntity, GeoPhotoDTO> implements GeoPhotoService {

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

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

        return wrapper;
    }

    @Override
    public PageData<GeoPhotoDTO> pageList(Map<String, Object> params) {
        PageData<GeoPhotoDTO> page = page(params);
        for (GeoPhotoDTO dto : page.getList()) {
            String geoSize = dto.getGeoSize();
            if (StrUtil.isNotBlank(geoSize)) {
                dto.setGeoSize(Utils.formatBytes(Long.parseLong(geoSize)));
            }
        }
        return page;
    }

    @Override
    public void upload(String type, MultipartFile file) {
        GeoPhotoEntity geoPhotoEntity = new GeoPhotoEntity();
        geoPhotoEntity.setGeoName(FileUtil.mainName(file.getOriginalFilename()));
        geoPhotoEntity.setGeoSize(file.getSize() + "");
        geoPhotoEntity.setGeoType(type);
        geoPhotoEntity.setStatus("0");
        baseDao.insert(geoPhotoEntity);
        if ("1".equals(type)) {//正射
            return;
        } else {//倾斜
            uploadMode(geoPhotoEntity, file);
        }
    }

    @Override
    public List<GeoPhotoDTO> getGeoList(String type) {
        QueryWrapper<GeoPhotoEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("geo_type", type);
        wrapper.eq("status", "1");
        wrapper.orderByDesc("create_date");
        List<GeoPhotoEntity> list = baseDao.selectList(wrapper);
        if ("1".equals(type)) {//正射
            return List.of();
        } else {//倾斜
            List<GeoPhotoDTO> dtoList = ConvertUtils.sourceToTarget(list, GeoPhotoDTO.class);
            for (GeoPhotoDTO dto : dtoList) {
                String geoName = dto.getGeoName();
                if (StrUtil.isNotBlank(geoName)) {
                    dto.setUrl(BusinessConstant.GEO_MODEL_DIR + "/" + geoName + "/tileset.json");
                }
            }
            return dtoList;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void remove(Long id) {
        try {
            GeoPhotoEntity geoPhotoEntity = selectById(id);
            if ("1".equals(geoPhotoEntity.getGeoType())) {
                return;
            } else {
                //删除数据
                deleteById(id);
                //删除文件
                File file = new File(BusinessConstant.GEO_MODEL_DIR + "/" + geoPhotoEntity.getGeoName());
                if (file.exists()) {
                    FileUtil.del(file);
                }
            }
        } catch (Exception e) {
            throw new RenException(ErrorCode.DELETE_FAIL, "地理资源");
        }
    }

    @Override
    public List<GeoPhotoDTO> getDiskMap() {
        List<GeoPhotoDTO> list = new ArrayList<>();
        File directory = new File(BusinessConstant.GEO_MODEL_DISK_UPLOAD_PATH);
        if (directory.exists() && directory.isDirectory()) {
            for (File file : Objects.requireNonNull(directory.listFiles())) {
                String name = file.getName();
                String extName = FileUtil.extName(name);
                GeoPhotoDTO dto = new GeoPhotoDTO();
                dto.setGeoName(name);
                dto.setGeoSize(Utils.formatBytes(file.length()));
                if ("tif".equals(extName)) {
                    dto.setGeoType("1");
                    list.add(dto);
                } else if ("zip".equals(extName)) {
                    dto.setGeoType("2");
                    list.add(dto);
                }
            }
        }
        return list;
    }

    @Override
    public void uploadModelDisk(List<String> names) {
        for (String name : names) {
            String sourceFile = BusinessConstant.GEO_MODEL_DISK_UPLOAD_PATH + File.separator + name;
            String extName = FileUtil.extName(name);
            if ("tif".equals(extName)) {
                return;
            } else if ("zip".equals(extName)) {
                File file = new File(sourceFile);
                if (file.exists()) {
                    GeoPhotoEntity entity = new GeoPhotoEntity();
                    entity.setGeoName(FileUtil.mainName(name));
                    entity.setGeoSize(file.length() + "");
                    entity.setGeoType("2");
                    entity.setStatus("0");
                    baseDao.insert(entity);
                    //解压文件
                    CompletableFuture.runAsync(() -> {
                        try (FileInputStream fis = new FileInputStream(file)) {
                            KmzUtils.unzipToDirectory(fis, Paths.get(BusinessConstant.GEO_MODEL_DIR));
                            entity.setStatus("1");
                        } catch (Exception e) {
                            entity.setStatus("2");
                            log.error("模型压缩文件解压失败，文件名称: {}", name, e);
                        } finally {
                            baseDao.updateById(entity);
                            // 删除临时文件
                            if (file.exists()) {
                                boolean delete = file.delete();
                            }
                        }
                    }).exceptionally(throwable -> {
                        log.error("模型压缩文件解压失败，文件名称: {}", name, throwable);
                        entity.setStatus("2");
                        baseDao.updateById(entity);
                        if (file.exists()) {
                            boolean delete = file.delete();
                        }
                        return null;
                    });
                }
            }
        }
    }

    //上传倾斜模型
    private void uploadMode(GeoPhotoEntity geoPhotoEntity, MultipartFile file) {
        // 创建临时文件
        File tempFile;
        try {
            tempFile = File.createTempFile("upload_geo_photo_" + System.currentTimeMillis(), ".zip");
            file.transferTo(tempFile);
        } catch (Exception e) {
            log.error("创建临时文件失败", e);
            geoPhotoEntity.setStatus("2");
            baseDao.updateById(geoPhotoEntity);
            return;
        }
        //解压文件
        File finalTempFile = tempFile;
        CompletableFuture.runAsync(() -> {
            try (FileInputStream fis = new FileInputStream(finalTempFile)) {
                KmzUtils.unzipToDirectory(fis, Paths.get(BusinessConstant.GEO_MODEL_DIR));
                geoPhotoEntity.setStatus("1");
            } catch (Exception e) {
                geoPhotoEntity.setStatus("2");
                log.error("模型压缩文件解压失败，文件名称: {}", file.getOriginalFilename(), e);
            } finally {
                baseDao.updateById(geoPhotoEntity);
                // 删除临时文件
                if (finalTempFile.exists()) {
                    boolean delete = finalTempFile.delete();
                }
            }
        }).exceptionally(throwable -> {
            log.error("模型压缩文件解压失败，文件名称: {}", file.getOriginalFilename(), throwable);
            geoPhotoEntity.setStatus("2");
            baseDao.updateById(geoPhotoEntity);
            if (finalTempFile.exists()) {
                boolean delete = finalTempFile.delete();
            }
            return null;
        });
    }
}