package com.sikey.wa04.business.application.service;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.sikey.wa04.business.domain.entity.UserId;
import com.sikey.wa04.business.infrastructure.configuration.AliyunOssConfig;
import com.sikey.wa04.business.infrastructure.dto.request.file.UploadFileRequest;
import com.sikey.wa04.business.infrastructure.dto.response.file.UploadFileResponse;
import com.sikey.wa04.common.exception.BusinessException;
import com.sikey.wa04.common.interceptor.CurrentRequestUser;
import com.sikey.wa04.common.utils.AESCryptoUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 阿里云OSS
 */
@Slf4j
@Service
public class AliyunOssService {

    @Resource
    private AliyunOssConfig aliyunOssConfig;

    @Value("${python.script.path}")
    private String pythonScriptPath;

    @Value("${python.input.dir}")
    private String inputDir;

    @Value("${python.output.dir}")
    private String outputDir;

    @Value("${python.user}")
    private String user;

    @Value("${python.env}")
    private String env;

    @Value("${python.pwd}")
    private String pwd;

    /**
     * 上传文件
     *
     * @param request 请求
     * @return file
     */
    public UploadFileResponse putObject(UploadFileRequest request) {
        Instant now = Instant.now();
        UserId userId = new UserId(CurrentRequestUser.get().getId());
        UploadFileResponse response = new UploadFileResponse();
        log.info("User {} upload file", userId);

        OSS ossClient = new OSSClientBuilder().build(aliyunOssConfig.getEndpoint(), aliyunOssConfig.getDefaultCredentialProvider());
        try (InputStream inputStream = request.getFile().getInputStream()) {

            String fileType = request.getFileType();

            // 生成图片位置数据，作为 oss 的 key
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.systemDefault());
            String date = dateTimeFormatter.format(now);
            String timestamp = String.valueOf(LocalDateTimeUtil.toEpochMilli(now));
            String encryptedUserId = new MD5().digestHex16(userId.getId());

            String key = date + "/" + encryptedUserId + "-" + timestamp + "." + fileType;
            String url = "https://" + aliyunOssConfig.getBucket() + "." + aliyunOssConfig.getEndpoint() + "/" + key;

            ossClient.putObject(aliyunOssConfig.getBucket(), key, inputStream);
            response.setUrl(url);

            log.info("[上传文件]fileType：{}", fileType);
            if (StringUtils.equalsAnyIgnoreCase(fileType, "png", "jpg", "bmp", "jpeg")) {
                String newImageNamePriFix = UUID.randomUUID().toString().replace("-", "") + System.currentTimeMillis();
                // 手表头像
                // 保存上传的图片到临时目录
                String tempFilePath = saveToTemp(request.getFile(), userId.toString(), newImageNamePriFix, fileType);
                log.info("[上传文件]临时图片文件tempFilePath：{}", tempFilePath);
                String processedImagePath = "";

                try {
                    // 调用 Python 脚本处理图片
                    processedImagePath = processImageWithPython(userId.toString(), newImageNamePriFix);

                    key = date + "/" + encryptedUserId + "-" + timestamp + "." + "bin";
                    String compressionUrl = "https://" + aliyunOssConfig.getBucket() + "." + aliyunOssConfig.getEndpoint() + "/" + key;
                    log.info("[上传文件]key：{}，compressionUrl：{}", key, compressionUrl);
                    // 上传到阿里云 OSS
                    ossClient.putObject(aliyunOssConfig.getBucket(), key, new File(processedImagePath));

                    response.setHisUrl(compressionUrl);
                } catch (Exception e) {
                    log.error("[上传文件]异常：{0}", e);
                    throw new BusinessException(HttpStatus.HTTP_INTERNAL_ERROR, "上传文件异常");
                } finally {
                    // 清理临时图片文件夹
                    Path tPath = Paths.get(tempFilePath).getParent();
                    deleteDirectory(tPath);
                    //Files.deleteIfExists(Paths.get(tempFilePath));之前是直接清理具体的文件，因为改成了文件夹每次都根据userId生成，所以改成清理文件夹
                    //清理临时的bin文件
                    Files.deleteIfExists(Paths.get(processedImagePath));
                }
            }

            log.info("[上传文件]返回值：{}", JSONUtil.toJsonPrettyStr(response));
        } catch (OSSException oe) {
            log.error("OSS error", oe);
            throw oe;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            ossClient.shutdown();
        }
        return response;
    }

    /**
     * 递归删除目录及其所有内容（包括空文件夹）
     * @param dir
     * @throws IOException
     */
    private static void deleteDirectory(Path dir) throws IOException {
        log.info("[上传文件]临时图片文件夹tPath：{}", dir.toString());
        if (!Files.exists(dir)) return;

        // 关键点1：使用反向排序，先删文件再删空目录
        Files.walk(dir)
                .sorted(Comparator.reverseOrder())
                .forEach(path -> {
                    try {
                        log.info("[上传文件]删除图片的文件或目录：{}", path);
                        Files.delete(path); // 删除文件或空目录
                    } catch (IOException e) {
                        throw new RuntimeException("删除失败: " + path, e);
                    }
                });
    }

    private String saveToTemp(MultipartFile file, String userId, String key, String fileType) throws IOException {
        // 1. 创建目标路径
        Path tempPath = Paths.get(inputDir + "/tset"+userId, key + "." + fileType);
        // 2. 确保父目录存在
        Files.createDirectories(tempPath.getParent());

        // 文件夹保存完成后,设置文件夹权限为777 (rwxrwxrwx)
        Set<PosixFilePermission> ppermissions = new HashSet<>();
        ppermissions.add(PosixFilePermission.OWNER_READ);
        ppermissions.add(PosixFilePermission.OWNER_WRITE);
        ppermissions.add(PosixFilePermission.OWNER_EXECUTE);
        ppermissions.add(PosixFilePermission.GROUP_READ);
        ppermissions.add(PosixFilePermission.GROUP_WRITE);
        ppermissions.add(PosixFilePermission.GROUP_EXECUTE);
        ppermissions.add(PosixFilePermission.OTHERS_READ);
        ppermissions.add(PosixFilePermission.OTHERS_WRITE);
        ppermissions.add(PosixFilePermission.OTHERS_EXECUTE);
        Files.setPosixFilePermissions(tempPath.getParent(), ppermissions);
        // 3. 保存文件
        file.transferTo(tempPath);

        // 文件保存完成后,设置文件权限为777 (rwxrwxrwx)
        Set<PosixFilePermission> permissions = new HashSet<>();
        permissions.add(PosixFilePermission.OWNER_READ);
        permissions.add(PosixFilePermission.OWNER_WRITE);
        permissions.add(PosixFilePermission.OWNER_EXECUTE);
        permissions.add(PosixFilePermission.GROUP_READ);
        permissions.add(PosixFilePermission.GROUP_WRITE);
        permissions.add(PosixFilePermission.GROUP_EXECUTE);
        permissions.add(PosixFilePermission.OTHERS_READ);
        permissions.add(PosixFilePermission.OTHERS_WRITE);
        permissions.add(PosixFilePermission.OTHERS_EXECUTE);
        Files.setPosixFilePermissions(tempPath, permissions);

        //返回文件保存路径的文件夹目录
        return tempPath.toString();
    }

    private String processImageWithPython(String userId, String originalFilename)
            throws Exception {

        // 生成输出文件路径
        String outputPath = outputDir + File.separator + originalFilename + ".bin";
        log.info("[上传文件]outputPath：{}", outputPath);

        // 构建完整的命令序列
        String fullCommand = String.format(
                "su - " + AESCryptoUtil.decrypt(user) + " -c 'source ~/.bashrc && conda activate " + AESCryptoUtil.decrypt(env) + " && python %s -p file -d cmp -c " + inputDir + "/config.xml -i %s -o %s -t include'",
                pythonScriptPath, inputDir + "/tset"+userId, outputDir
        );
         log.info("[上传文件]fullCommand：{}", fullCommand);
        // 准备执行命令
        List<String> command = new ArrayList<>();
        command.add("bash");
        command.add("-c");
        command.add("echo '" + AESCryptoUtil.decrypt(pwd) + "' | " + fullCommand);

        // 执行命令
        ProcessBuilder processBuilder = new ProcessBuilder(command);
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();
        log.info("[上传文件]等待执行开始");
        // 等待执行完成
        if (!process.waitFor(30, TimeUnit.SECONDS)) {
            process.destroy();
            throw new RuntimeException("Python 脚本执行超时");
        }
        log.info("[上传文件]等待执行结束，outputPath：{}", outputPath);
        return outputPath;
    }

}
