package cn.iocoder.yudao.module.system.service.positionmap;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.positionmap.vo.PositionMapPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.positionmap.vo.PositionMapSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.information.DeviceInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.positionmap.PositionMapDO;
import cn.iocoder.yudao.module.system.dal.mysql.positionmap.PositionMapMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;
import org.yaml.snakeyaml.Yaml;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;

/**
 * 仓库点位地图 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class PositionMapServiceImpl extends ServiceImpl<PositionMapMapper, PositionMapDO> implements PositionMapService {

    @Resource
    private PositionMapMapper positionMapMapper;
    @Value("${map.file.upload-path}")
    private String UPLOAD_DIR;

    @Override
    public Long createPositionMap(PositionMapSaveReqVO createReqVO) {
        // 插入
        PositionMapDO positionMap = BeanUtils.toBean(createReqVO, PositionMapDO.class);
        positionMapMapper.insert(positionMap);
        // 返回
        return positionMap.getId();
    }

    @Override
    public void updatePositionMap(PositionMapSaveReqVO updateReqVO) {
        // 更新
        PositionMapDO updateObj = BeanUtils.toBean(updateReqVO, PositionMapDO.class);
        positionMapMapper.updateById(updateObj);
    }

    @Override
    public void deletePositionMap(Long id) {
        // 删除
        positionMapMapper.deleteById(id);
    }


    @Override
    public PositionMapDO getPositionMap(Long id) {
        return positionMapMapper.selectById(id);
    }

    @Override
    public PageResult<PositionMapDO> getPositionMapPage(PositionMapPageReqVO pageReqVO) {
        return positionMapMapper.selectPage(pageReqVO);
    }

    @Override
    public void uploadFiles(MultipartFile[] files) {
        if (files == null || files.length < 2) {
            throw exception(PLEASE_UPLOAD_PNG_AND_YAML_FILES);
        }
        MultipartFile yamlFile = null;
        MultipartFile pngFile = null;
        // 分类文件
        for (MultipartFile file : files) {
            if (Objects.requireNonNull(file.getOriginalFilename()).toLowerCase().endsWith(".yaml")) {
                yamlFile = file;
            } else if (Objects.requireNonNull(file.getOriginalFilename()).toLowerCase().endsWith(".png")) {
                pngFile = file;
            }
        }
        if (yamlFile == null || pngFile == null || yamlFile.isEmpty() || pngFile.isEmpty()) {
            throw exception(PLEASE_UPLOAD_PNG_AND_YAML_FILES);
        }
        // 处理 YAML 文件 - 返回yaml中存储的楼层信息 作为png文件存储的目录
        String url = null;
        String yamlJson = null;
        String yamlUrl = null;
        String pngUrl = null;
        Integer floorInt = null;
        String areaStr = null;
        try {
            // Get the file and save it somewhere
            byte[] bytes = yamlFile.getBytes();
            // Parse the YAML file
            Yaml yaml = new Yaml();
            Map<String, Object> data = yaml.load(new String(bytes));
            Object floor = data.get("floor");
            Object area = data.get("area");
            // -- 根据楼和区域查询是否有存在地图信息 - 如果有存在则替换 - 并且通知所有该楼该区域的AGV
            if (ObjUtil.isEmpty(floor) || ObjUtil.isEmpty(area)) {
                throw exception(AGV_UPLOAD_INFORMATION_DOES_NOT_INCLUDE_FLOOR_OR_AREA_INFORMATION);
            }
            floorInt = Integer.parseInt(Objects.requireNonNull(floor).toString());
            areaStr = Objects.requireNonNull(area).toString();
            url = UPLOAD_DIR + floor + "/" + area + "/";
            // 判断目录是否存在 - 如果不存在则创建目录
            File directory = new File(url);
            if (!directory.exists()) {
                directory.mkdirs();
            }
            yamlUrl = url + yamlFile.getOriginalFilename();
            Path path = Paths.get(yamlUrl);
            Files.write(path, bytes);
            // Convert the YAML data to JSON
            ObjectMapper objectMapper = new ObjectMapper();
            yamlJson = objectMapper.writeValueAsString(data);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("Failed to upload '{}'", e);
            return;
        }

        // 处理 PNG 文件
        try {
            pngUrl = url + pngFile.getOriginalFilename();
            byte[] bytes = pngFile.getBytes();
            Path path = Paths.get(pngUrl);
            Files.write(path, bytes);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        PositionMapDO positionMap = positionMapMapper.selectOne(new LambdaQueryWrapperX<PositionMapDO>()
                .eq(PositionMapDO::getFloor, floorInt)
                .eq(PositionMapDO::getArea, areaStr));
        if (positionMap != null) {
            positionMapMapper.updateById(positionMap.setYamlJson(yamlJson).setPngUrl(pngUrl).setYamlUrl(yamlUrl));

            // todo 异步通知所有该楼层区域内所有AGV车地图有更改 - 这个时候AGV应当重新获取地图信息
        } else {
            positionMap = new PositionMapDO().setFloor(floorInt).setArea(areaStr)
                    .setYamlJson(yamlJson).setPngUrl(pngUrl).setYamlUrl(yamlUrl);
            positionMapMapper.insert(positionMap);
        }
    }

    @Override
    public void downloadFile(Integer floor, String area, HttpServletResponse response) throws IOException {
        PositionMapDO positionMapDO = positionMapMapper.selectOne(new LambdaQueryWrapperX<PositionMapDO>()
                .eq(PositionMapDO::getFloor, floor)
                .eq(PositionMapDO::getArea, area));
        if (positionMapDO == null) {
            throw exception(AGV_MAP_NOT_FOUND);
        }

        // 假设文件存储路径，根据实际情况修改
        String basePath = UPLOAD_DIR + floor + "/" + area + "/";
        List<File> filesToZip = findFilesByFloorAndArea(basePath);

        if (filesToZip.isEmpty()) {
            response.sendError(HttpStatus.NOT_FOUND.value(), "No files found for the given floor and area.");
            return;
        }

        response.setContentType("application/zip");
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"files.zip\"");

        try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
            for (File file : filesToZip) {
                ZipEntry zipEntry = new ZipEntry(file.getName());
                zipOut.putNextEntry(zipEntry);
                try (FileInputStream fileIn = new FileInputStream(file)) {
                    IOUtils.copy(fileIn, zipOut);
                }
                zipOut.closeEntry();
            }
        }
    }

    private List<File> findFilesByFloorAndArea(String basePath) {
        List<File> foundFiles = new ArrayList<>();
        File directory = new File(basePath);
        if (directory.exists() && directory.isDirectory()) {
            File[] files = directory.listFiles();
            if (files != null) {
                foundFiles.addAll(Arrays.asList(files));
            }
        }
        return foundFiles;
    }


    @Override
    public void downloadFile(HttpServletResponse response) throws IOException {
        // 指定要压缩的文件夹路径
        Path folderPath = Paths.get(UPLOAD_DIR);

        if (!Files.exists(folderPath) || !Files.isDirectory(folderPath)) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Folder not found");
            return;
        }

        // 设置响应头
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=project.zip");

        // 压缩文件夹并写入响应体
        try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
            addFolderToZip(folderPath.toFile(), folderPath.toString(), zipOut);
        }
    }


    private void addFolderToZip(File folder, String baseFolder, ZipOutputStream zipOut) throws IOException {
        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    addFolderToZip(file, baseFolder, zipOut);
                    continue;
                }
                FileInputStream fis = new FileInputStream(file);
                ZipEntry zipEntry = new ZipEntry(file.getAbsolutePath().substring(baseFolder.length() + 1));
                zipOut.putNextEntry(zipEntry);

                byte[] bytes = new byte[1024];
                int length;
                while ((length = fis.read(bytes)) >= 0) {
                    zipOut.write(bytes, 0, length);
                }
                fis.close();
            }
        }
    }


    @Override
    @Cacheable(value = "pngBase64", key = "#floor + '_' + #area")
    public String downloadPngBase64(Integer floor, String area) {
        // 根据楼层和区域查找对应的PNG文件路径
        String basePath = UPLOAD_DIR + floor + "/" + area + "/";
        File directory = new File(basePath);
        if (!directory.exists() || !directory.isDirectory()) {
            log.error("Directory not found for floor: {}, area: {}", floor, area);
            throw exception(AGV_MAP_NOT_FOUND);
        }

        // 查找PNG文件
        File pngFile = null;
        File[] files = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".png"));
        if (files != null && files.length > 0) {
            pngFile = files[0]; // 假设每个目录下只有一个PNG文件
        }

        if (pngFile == null || !pngFile.exists()) {
            log.error("PNG file not found for floor: {}, area: {}", floor, area);
            throw exception(AGV_MAP_NOT_FOUND);
        }
        String encodedString = null;
        try {
            // 读取文件内容
            byte[] fileContent = Files.readAllBytes(pngFile.toPath());
            // 将文件内容转换为Base64字符串
            encodedString = Base64.getEncoder().encodeToString(fileContent);
            // 添加MIME类型前缀
            encodedString = "data:image/png;base64," + encodedString;
        } catch (IOException e) {
            log.error("Failed to convert PNG to Base64 for floor: {}, area: {}", floor, area, e);
            throw exception(AGV_IMAGE_CONVERSION_TO_BASE64_FAILED);
        }
        return encodedString;
    }

    @Override
    public Map<Integer, List<PositionMapDO>> getAllMap() {
        List<PositionMapDO> positionMapDOS = positionMapMapper.selectList(new LambdaQueryWrapperX<PositionMapDO>()
                .orderByAsc(PositionMapDO::getFloor));

        if (ObjectUtil.isEmpty(positionMapDOS)) {
            return new HashMap<>();
        }

        Map<Integer, List<PositionMapDO>> collect =
                positionMapDOS.stream()
                        .collect(Collectors.groupingBy(PositionMapDO::getFloor));

        ArrayList<Map.Entry<Integer, List<PositionMapDO>>> list = new ArrayList<Map.Entry<Integer, List<PositionMapDO>>>(collect.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<Integer,  List<PositionMapDO>>>() {
            public int compare(Map.Entry<Integer,  List<PositionMapDO>> o1, Map.Entry<Integer,  List<PositionMapDO>> o2) {
                return o1.getKey().compareTo(o2.getKey()); // 按照值排序
            }
        });

        return collect;
    }

    @Override
    public void emergencyStopOrRecovery(Long id) {
        // 先获取当前地图上的所有AGV信息

    }

}
