package com.ruoyi.wayline.controller;

import com.ruoyi.system.domain.FlightPaths;
import com.ruoyi.system.domain.ManageDeviceDictionary;
import com.ruoyi.system.service.IFlightPathsService;
import com.ruoyi.system.service.IManageDeviceDictionaryService;
import com.ruoyi.wayline.domain.AddWaypointVo;
import com.ruoyi.wayline.domain.RenameVo;
import com.ruoyi.wayline.domain.WaypointData;
import com.ruoyi.wayline.utils.HttpResult;
import com.ruoyi.wayline.utils.MD5Util;
import com.ruoyi.wayline.waypoint.WayPointKmlProcessorModify;
import com.ruoyi.wayline.waypoint.WayPointWpmlProcessorModify;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.zip.*;

@Slf4j
@RestController
@Api(tags = "航线文件管理")
public class WaypointController {

    @Resource
    private IFlightPathsService flightPathsService;
    @Resource
    private IManageDeviceDictionaryService manageDeviceDictionaryService;
    @Resource
    private Environment environment;

    private static final String UPLOAD_DIR = System.getProperty("user.dir") + "/upload";

    // 工具方法：使用 flag 和 fileName 组合计算 MD5 值
    private String calculateMD5(String flag, String fileName) {
        String input = flag + fileName;
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) sb.append(String.format("%02x", b));
        log.info("MD5: {}", sb.toString());
        return sb.toString();
    }

    // 工具方法：检查文件是否已存在并返回错误信息
    private ResponseEntity<HttpResult> checkFileExistence(String fileName, String flag) {
        FlightPaths flightPaths = new FlightPaths();
        flightPaths.setFileName(fileName);
        flightPaths.setFlag(flag);
        List<FlightPaths> pathsList = flightPathsService.selectByCondition(flightPaths);
        if (pathsList != null && !pathsList.isEmpty()) {
            HttpResult httpResult = HttpResult.error();
            httpResult.setMessage("文件名重复，请重新命名");
            return ResponseEntity.ok(httpResult);
        }
        return null;
    }

    private boolean checkFileExistenceWithBool(String fileName, String flag) {
        FlightPaths flightPaths = new FlightPaths();
        flightPaths.setFileName(fileName);
        flightPaths.setFlag(flag);
        List<FlightPaths> pathsList = flightPathsService.selectByCondition(flightPaths);
        return pathsList != null && !pathsList.isEmpty();
    }

    @ApiOperation("导入航线文件")
    @PostMapping("/dj/router/import")
    public ResponseEntity<HttpResult> importWaypoint(@RequestParam("file") MultipartFile file, @RequestParam("flag") String flag) {
        if (file.isEmpty()) {
            HttpResult httpResult = HttpResult.error();
            httpResult.setMessage("文件为空");
            return ResponseEntity.ok(httpResult);
        }

        try {
            String fileName = file.getOriginalFilename();
            ResponseEntity<HttpResult> existenceCheck = checkFileExistence(fileName, flag);
            if (existenceCheck != null) return existenceCheck;

            // 创建上传目录
            Path uploadPath = Paths.get(UPLOAD_DIR);
            Files.createDirectories(uploadPath);

            byte[] fileBytes = file.getBytes();
            String md5 = calculateMD5(flag, fileName);  // 使用 flag 和 fileName 组合生成 MD5
            String md5FileNameWithExtension = md5 + ".kmz";
            Path serverPath = uploadPath.resolve(md5FileNameWithExtension);

            Files.write(serverPath, fileBytes);

            String downloadLink = buildDownloadLink(md5FileNameWithExtension);
            saveFlightPath(fileName, flag, md5, downloadLink, true);

            HttpResult httpResult = HttpResult.ok();
            httpResult.setMessage("文件导入成功");
            return ResponseEntity.ok(httpResult);
        } catch (Exception e) {
            log.error("文件保存失败: {}", e.getMessage());
            HttpResult httpResult = HttpResult.error();
            httpResult.setMessage("保存失败");
            return ResponseEntity.ok(httpResult);
        }
    }

    private void saveFlightPath(String fileName, String flag, String md5, String fileUrl, boolean isImport) {
        FlightPaths flightPath = new FlightPaths();
        flightPath.setFileName(fileName);
        flightPath.setFlag(flag);
        flightPath.setFileMd5(md5);
        flightPath.setFileUrl(fileUrl);
        flightPath.setIsImport(isImport ? "true" : "false");
        flightPath.setCreatedAt(new Date());
        flightPath.setUpdatedAt(new Date());
        flightPathsService.insertFlightPaths(flightPath);
    }

    private String buildDownloadLink(String md5FileNameWithExtension) throws IOException {
        String ip = environment.getProperty("server.host", InetAddress.getLocalHost().getHostAddress());
        int port = environment.getProperty("server.port", Integer.class, 8080);
        return "http://" + ip + ":" + port + "/dj/router/download/" + md5FileNameWithExtension;
    }

    @ApiOperation("新增航线文件")
    @PostMapping("/dj/router/add")
    public ResponseEntity<HttpResult> addWaypoint(@RequestBody AddWaypointVo addWaypointVo) {
        ResponseEntity<HttpResult> existenceCheck = checkFileExistence(addWaypointVo.getFilename(), addWaypointVo.getFlag());
        if (existenceCheck != null) return existenceCheck;

        FlightPaths flightPaths = new FlightPaths();
        flightPaths.setFileName(addWaypointVo.getFilename());
        flightPaths.setFlag(addWaypointVo.getFlag());
        flightPaths.setIsImport("false");
        flightPaths.setGlobalPointHeight(addWaypointVo.getGlobalPointHeight());
        flightPaths.setCreatedAt(new Date());
        flightPaths.setUpdatedAt(new Date());

        // 获取飞机类型
        String[] split = addWaypointVo.getRemark().split("-");
        ManageDeviceDictionary manageDeviceDictionary = manageDeviceDictionaryService
                .selectManageDeviceDictionaryByCondition(split[0], split[1], split[2]);
        flightPaths.setDeviceType(manageDeviceDictionary.getDeviceName());

        flightPathsService.insertFlightPaths(flightPaths);
        return ResponseEntity.ok(HttpResult.ok());
    }

    @ApiOperation("根据参数追数据到加航线文件")
    @PostMapping("/dj/router/waypoint")
    public ResponseEntity<HttpResult> downloadWaypoint(@RequestBody WaypointData dataRequest) {
        HttpResult httpResult = HttpResult.ok();
        Map<String, Object> map = new HashMap<>();
        String fileName = flightPathsService.selectFlightPathsById(Long.valueOf(dataRequest.getId())).getFileName();
        Path uploadPath = Paths.get(UPLOAD_DIR);

        try {
            Files.createDirectories(uploadPath);
            String md5FileName = calculateMD5(dataRequest.getFlag(), fileName);  // 使用 flag 和 fileName 组合生成 MD5
            String md5FileNameWithExtension = md5FileName + ".kmz";
            Path serverPath = uploadPath.resolve(md5FileNameWithExtension);

            // 日志：显示生成的文件路径
            log.info("下载航线文件路径: {}", serverPath);

            // 创建并压缩文件
            try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                 ZipOutputStream zipOut = new ZipOutputStream(byteArrayOutputStream)) {

                byte[] file1 = new WayPointKmlProcessorModify().processKml(dataRequest, new Random().nextInt(9000) + 1000);
                zipOut.putNextEntry(new ZipEntry("wpmz/template.kml"));
                zipOut.write(file1);
                zipOut.closeEntry();

                byte[] file2 = new WayPointWpmlProcessorModify().processWpml(dataRequest, new Random().nextInt(9000) + 1000);
                zipOut.putNextEntry(new ZipEntry("wpmz/waylines.wpml"));
                zipOut.write(file2);
                zipOut.closeEntry();

                zipOut.finish();
                Files.write(serverPath, byteArrayOutputStream.toByteArray());
            }

            // 构建下载链接并更新数据库
            String downloadLink = buildDownloadLink(md5FileNameWithExtension);
            map.put("downloadLink", downloadLink);
            map.put("flag", true);
            map.put("signature", md5FileName);
            httpResult.setData(map);
            httpResult.setMessage("生成航线文件并转存至服务器成功!");

            // 更新数据库中的航线文件记录，保存生成的MD5值
            FlightPaths flightPaths = new FlightPaths();
            flightPaths.setId(Long.valueOf(dataRequest.getId()));
            flightPaths.setFileName(fileName);
            flightPaths.setFileUrl(downloadLink);
            flightPaths.setFileMd5(md5FileName);  // 存储 MD5 值
            flightPaths.setPhotoNum(Long.valueOf(dataRequest.getPhotoNum()));
            flightPaths.setEstimateTime(dataRequest.getEstimateTime());
            flightPaths.setType(dataRequest.getType());
            flightPaths.setWaylineLen(dataRequest.getWaylineLen());
            flightPaths.setPoints(dataRequest.getPoints());
            flightPaths.setUpdatedAt(new Date());
            flightPathsService.updateFlightPaths(flightPaths);

        } catch (Exception e) {
            httpResult = HttpResult.error();
            httpResult.setMessage("生成航线文件并转存至服务器失败,原因:" + e.getMessage());
        }
        return ResponseEntity.ok(httpResult);
    }

    @ApiOperation("下载航线文件")
    @GetMapping("/dj/router/download/{fileName}")
    public ResponseEntity<byte[]> downloadFile(@PathVariable String fileName) {
        Path filePath = Paths.get(UPLOAD_DIR, fileName);
        if (!Files.exists(filePath)) {
            return ResponseEntity.notFound().build();
        }
        try {
            byte[] fileContent = Files.readAllBytes(filePath);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDisposition(ContentDisposition.builder("attachment").filename(fileName, StandardCharsets.UTF_8).build());
            return ResponseEntity.ok().headers(headers).body(fileContent);
        } catch (IOException e) {
            log.error("文件下载失败, 原因: {}", e.getMessage());
            return ResponseEntity.status(500).build();
        }
    }

    @ApiOperation("复制航线文件")
    @PostMapping("/dj/router/copy/{id}")
    public ResponseEntity<HttpResult> copyWaypoint(@PathVariable Long id) {
        FlightPaths originalFlightPaths = flightPathsService.selectFlightPathsById(id);
        if (originalFlightPaths == null) return ResponseEntity.notFound().build();

        String originalFileName = originalFlightPaths.getFileName();
        String originalMd5 = originalFlightPaths.getFileMd5();
        Path originalFilePath = Paths.get(UPLOAD_DIR, originalMd5 + ".kmz");

        // 日志：显示待复制的原始文件路径
        log.info("复制航线文件路径: {}", originalFilePath);

        if (!Files.exists(originalFilePath)) {
            HttpResult httpResult = HttpResult.error();
            httpResult.setMessage("请先生成航线文件再执行复制");
            return ResponseEntity.ok(httpResult);
        }

        String newFileName = originalFileName + "(1)";

        // 查询文件是否已存在
        if (checkFileExistenceWithBool(newFileName, originalFlightPaths.getFlag())) {
            HttpResult httpResult = HttpResult.error();
            httpResult.setMessage("航线文件" + newFileName + "已存在");
            return ResponseEntity.ok(httpResult);
        }

        String newMd5 = calculateMD5(originalFlightPaths.getFlag(), newFileName);
        Path newFilePath = Paths.get(UPLOAD_DIR, newMd5 + ".kmz");

        try {
            Files.copy(originalFilePath, newFilePath);
            FlightPaths newFlightPaths = new FlightPaths();
            newFlightPaths.setFileName(newFileName);
            newFlightPaths.setFileUrl(buildDownloadLink(newMd5 + ".kmz"));
            newFlightPaths.setFileMd5(newMd5);
            newFlightPaths.setFlag(originalFlightPaths.getFlag());
            newFlightPaths.setIsImport(originalFlightPaths.getIsImport());
            newFlightPaths.setPoints(originalFlightPaths.getPoints());
            newFlightPaths.setDeviceType(originalFlightPaths.getDeviceType());
            newFlightPaths.setPhotoNum(originalFlightPaths.getPhotoNum());
            newFlightPaths.setEstimateTime(originalFlightPaths.getEstimateTime());
            newFlightPaths.setType(originalFlightPaths.getType());
            newFlightPaths.setWaylineLen(originalFlightPaths.getWaylineLen());
            newFlightPaths.setRemark(originalFlightPaths.getRemark());

            newFlightPaths.setCreatedAt(new Date());
            newFlightPaths.setUpdatedAt(new Date());

            flightPathsService.insertFlightPaths(newFlightPaths);

            HttpResult httpResult = HttpResult.ok();
            httpResult.setMessage("复制航线文件成功!");
            return ResponseEntity.ok(httpResult);
        } catch (IOException e) {
            HttpResult httpResult = HttpResult.error();
            httpResult.setMessage("复制航线文件失败, 原因:" + e.getMessage());
            return ResponseEntity.ok(httpResult);
        }
    }

    @ApiOperation("重命名航线文件")
    @PostMapping("/dj/router/rename")
    public ResponseEntity<HttpResult> renameWaypoint(@RequestBody RenameVo renameVo) {
        FlightPaths flightPaths = flightPathsService.selectFlightPathsById(renameVo.getId());
        if (flightPaths == null) return ResponseEntity.notFound().build();

        // 如果文件名称和现在已有的名称相同则不进行任何处理
        if (flightPaths.getFileName().equals(renameVo.getNewFileName())) {
            HttpResult httpResult = HttpResult.ok();
            return ResponseEntity.ok(httpResult);
        }
        if (checkFileExistenceWithBool(renameVo.getNewFileName(), flightPaths.getFlag())) {
            HttpResult httpResult = HttpResult.error();
            httpResult.setMessage("航线文件" + renameVo.getNewFileName() + "已存在，请重新命名");
            return ResponseEntity.ok(httpResult);
        }

        String newMd5 = calculateMD5(flightPaths.getFlag(), renameVo.getNewFileName());
        String originalMd5 = flightPaths.getFileMd5();
        Path originalFilePath = Paths.get(UPLOAD_DIR, originalMd5 + ".kmz");
        Path newFilePath = Paths.get(UPLOAD_DIR, newMd5 + ".kmz");

        try {
            Files.move(originalFilePath, newFilePath);
            flightPaths.setFileName(renameVo.getNewFileName());
            flightPaths.setFileUrl(buildDownloadLink(newMd5 + ".kmz"));
            flightPaths.setFileMd5(newMd5);
            flightPaths.setGlobalPointHeight(flightPaths.getGlobalPointHeight());
            flightPaths.setUpdatedAt(new Date());
            flightPathsService.updateFlightPaths(flightPaths);

            HttpResult httpResult = HttpResult.ok();
            httpResult.setMessage("重命名航线文件成功!");
            return ResponseEntity.ok(httpResult);
        } catch (IOException e) {
            HttpResult httpResult = HttpResult.error();
            httpResult.setMessage("重命名航线文件失败, 原因: " + e.getMessage());
            return ResponseEntity.ok(httpResult);
        }
    }
}
