package com.rj.service.impl;

import com.rj.common.AudioManagementConstants;
import com.rj.entity.AudioManagement;
import com.rj.service.IAudioManagementService;
import com.rj.service.ISoundRecordingUploadService;
import com.rj.service.MinIOService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import java.util.regex.Pattern;

/**
 * 录音上传服务实现类
 * 
 * @author rj
 * @date 2025-01-03
 */
@Slf4j
@Service
public class SoundRecordingUploadServiceImpl implements ISoundRecordingUploadService {

    @Autowired
    private MinIOService minIOService;

    @Autowired
    private IAudioManagementService audioManagementService;

    @Value("${app.audio.upload.path:uploads/audio}")
    private String uploadPath;

    /**
     * 分片索引正则表达式
     * A开头：启动/运行段，如A001, A005
     * Z开头：停止段，Z后面数字表示总段数，如Z999表示总共999段，Z001表示只有一段
     */
    private static final Pattern CHUNK_INDEX_PATTERN = Pattern.compile("^[AZ]\\d{3}$");
    
    /**
     * 时间格式正则表达式（yyMMddHHmmss）
     */
    private static final Pattern TIME_PATTERN = Pattern.compile("^\\d{12}$");

    @Override
    public ResponseData uploadRecording(String deviceNo, String deviceType, MultipartFile soundRecording,
                                        String fileName, String chunkIndex, String startTime, String endTime, String usrNo) {
        
        log.info("开始处理录音上传 - deviceNo: {}, fileName: {}, chunkIndex: {}, startTime: {}, endTime: {}", 
                deviceNo, fileName, chunkIndex, startTime, endTime);
        
        try {
            // 参数验证
            String validationError = validateParameters(deviceNo, soundRecording, fileName, chunkIndex, startTime, endTime);
            if (validationError != null) {
                log.warn("参数验证失败 - deviceNo: {}, error: {}", deviceNo, validationError);
                return createErrorResponse(400, 1, validationError);
            }
            
            // 解析分片信息
            ChunkInfo chunkInfo = parseChunkIndex(chunkIndex);
            log.info("分片信息解析 - prefix: {}, chunkNumber: {}, isLastChunk: {}, totalChunks: {}", 
                    chunkInfo.prefix, chunkInfo.chunkNumber, chunkInfo.isLastChunk, chunkInfo.totalChunks);
            
            // 上传文件到 MinIO（已注释，用于对比本地保存耗时）
            // String minioUrl;
            // try {
            //     minioUrl = minIOService.uploadFile(soundRecording);
            //     log.info("文件上传到MinIO成功 - deviceNo: {}, fileName: {}, minioUrl: {}", deviceNo, fileName, minioUrl);
            // } catch (Exception e) {
            //     log.error("文件上传到MinIO失败 - deviceNo: {}, fileName: {}, error: {}", deviceNo, fileName, e.getMessage(), e);
            //     return createErrorResponse(500, 1, "文件上传失败：" + e.getMessage());
            // }
            
            // 保存文件到本地
            String localFilePath;
            try {
                localFilePath = saveFileToLocal(soundRecording, fileName, deviceNo);
                log.info("文件保存到本地成功 - deviceNo: {}, fileName: {}, localPath: {}", deviceNo, fileName, localFilePath);
            } catch (Exception e) {
                log.error("文件保存到本地失败 - deviceNo: {}, fileName: {}, error: {}", deviceNo, fileName, e.getMessage(), e);
                return createErrorResponse(500, 1, "文件保存失败：" + e.getMessage());
            }
            
            // 转换时间格式（yyMMddHHmmss -> LocalDateTime）
            LocalDateTime recordingStartTime = parseTimeString(startTime);
            LocalDateTime recordingEndTime = parseTimeString(endTime);
            
            // 计算录音时长（分钟）
            BigDecimal duration = calculateDuration(recordingStartTime, recordingEndTime);
            
            // 获取文件扩展名
            String fileExtension = getFileExtension(fileName);
            
            // 创建录音管理记录
            AudioManagement audioManagement = new AudioManagement();
            audioManagement.setId(UUID.randomUUID().toString());
            audioManagement.setRecordingName(fileName);
            audioManagement.setRecordingTime(recordingStartTime);
            audioManagement.setDuration(duration);
            // audioManagement.setAudioFileUrl(minioUrl);  // MinIO URL，已注释
            audioManagement.setAudioFilePath(localFilePath);  // 本地文件路径
            audioManagement.setAudioFileSize(soundRecording.getSize());
            audioManagement.setAudioFileOriginalName(fileName);
            audioManagement.setAudioFileExtension(fileExtension);
            audioManagement.setUploadTime(LocalDateTime.now());
            audioManagement.setUploadStatus("已上传");
            audioManagement.setServiceStatus(AudioManagementConstants.SERVICE_STATUS_IN_SERVICE);
            audioManagement.setIsMerged(chunkInfo.isLastChunk && chunkInfo.totalChunks > 1);
            
            // 如果有自定义编号，可以存储到备注中
            if (StringUtils.hasText(usrNo)) {
                audioManagement.setRemarks("设备编号: " + usrNo + ", 设备类型: " + (deviceType != null ? deviceType : "未知"));
            }
            
            // 设置创建和更新时间
            LocalDateTime now = LocalDateTime.now();
            audioManagement.setCreateTime(now);
            audioManagement.setUpdateTime(now);
            
            // 保存到数据库
            boolean saved = audioManagementService.save(audioManagement);
            if (!saved) {
                log.error("保存录音记录到数据库失败 - deviceNo: {}, fileName: {}", deviceNo, fileName);
                return createErrorResponse(500, 1, "保存录音记录失败");
            }
            
            log.info("录音文件上传成功并保存到数据库 - deviceNo: {}, fileName: {}, fileSize: {} bytes, audioId: {}", 
                    deviceNo, fileName, soundRecording.getSize(), audioManagement.getId());
            
            // 返回成功响应
            return createSuccessResponse();
            
        } catch (Exception e) {
            log.error("录音上传处理异常 - deviceNo: {}, fileName: {}, error: {}", 
                    deviceNo, fileName, e.getMessage(), e);
            return createErrorResponse(500, 1, "服务器内部错误：" + e.getMessage());
        }
    }
    
    /**
     * 验证参数
     */
    private String validateParameters(String deviceNo, MultipartFile soundRecording, String fileName,
                                     String chunkIndex, String startTime, String endTime) {
        
        if (deviceNo == null || deviceNo.trim().isEmpty()) {
            return "设备序列号不能为空";
        }
        
        if (soundRecording == null || soundRecording.isEmpty()) {
            return "录音文件不能为空";
        }
        
        if (fileName == null || fileName.trim().isEmpty()) {
            return "文件名称不能为空";
        }
        
        if (chunkIndex == null || chunkIndex.trim().isEmpty()) {
            return "分片索引不能为空";
        }
        
        // 验证分片索引格式
        if (!CHUNK_INDEX_PATTERN.matcher(chunkIndex).matches()) {
            return "分片索引格式错误，应为A开头或Z开头后跟3位数字，如A001或Z999";
        }
        
        if (startTime == null || startTime.trim().isEmpty()) {
            return "录音开始时间不能为空";
        }
        
        if (!TIME_PATTERN.matcher(startTime).matches()) {
            return "录音开始时间格式错误，应为yyMMddHHmmss，如240510130000";
        }
        
        if (endTime == null || endTime.trim().isEmpty()) {
            return "录音结束时间不能为空";
        }
        
        if (!TIME_PATTERN.matcher(endTime).matches()) {
            return "录音结束时间格式错误，应为yyMMddHHmmss，如240510135959";
        }
        
        return null;
    }
    
    /**
     * 解析分片索引
     */
    private ChunkInfo parseChunkIndex(String chunkIndex) {
        char prefix = chunkIndex.charAt(0);
        int chunkNumber = Integer.parseInt(chunkIndex.substring(1));
        boolean isLastChunk = prefix == 'Z';
        int totalChunks = isLastChunk ? chunkNumber : 0;
        
        return new ChunkInfo(prefix, chunkNumber, isLastChunk, totalChunks);
    }
    
    /**
     * 创建成功响应
     */
    private ResponseData createSuccessResponse() {
        ResponseData response = new ResponseData();
        response.code = 200;
        response.status = 0;
        response.success = true;
        response.message = "请求成功";
        response.timestamp = Instant.now().getEpochSecond();
        return response;
    }
    
    /**
     * 创建错误响应
     */
    private ResponseData createErrorResponse(int code, int status, String message) {
        ResponseData response = new ResponseData();
        response.code = code;
        response.status = status;
        response.success = false;
        response.message = message;
        response.timestamp = Instant.now().getEpochSecond();
        return response;
    }
    
    /**
     * 解析时间字符串（yyMMddHHmmss -> LocalDateTime）
     */
    private LocalDateTime parseTimeString(String timeStr) {
        try {
            // 将 yy 转换为 yyyy（假设 yy < 50 表示 20xx，否则表示 19xx）
            int year = Integer.parseInt(timeStr.substring(0, 2));
            if (year < 50) {
                year += 2000;
            } else {
                year += 1900;
            }
            
            // 构造完整的日期时间字符串
            String fullTimeStr = String.format("%04d%s", year, timeStr.substring(2));
            DateTimeFormatter fullFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
            return LocalDateTime.parse(fullTimeStr, fullFormatter);
        } catch (Exception e) {
            log.error("时间格式解析失败 - timeStr: {}, error: {}", timeStr, e.getMessage());
            return LocalDateTime.now();
        }
    }
    
    /**
     * 计算录音时长（分钟）
     */
    private BigDecimal calculateDuration(LocalDateTime startTime, LocalDateTime endTime) {
        if (startTime == null || endTime == null) {
            return BigDecimal.ZERO;
        }
        
        long seconds = java.time.Duration.between(startTime, endTime).getSeconds();
        if (seconds < 0) {
            log.warn("录音结束时间早于开始时间，返回0");
            return BigDecimal.ZERO;
        }
        
        // 转换为分钟，保留2位小数
        double minutes = seconds / 60.0;
        return BigDecimal.valueOf(minutes).setScale(2, java.math.RoundingMode.HALF_UP);
    }
    
    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        if (!StringUtils.hasText(filename)) {
            return "";
        }
        int lastDotIndex = filename.lastIndexOf('.');
        if (lastDotIndex > 0 && lastDotIndex < filename.length() - 1) {
            return filename.substring(lastDotIndex + 1).toLowerCase();
        }
        return "";
    }
    
    /**
     * 保存文件到本地
     * 
     * @param file 文件
     * @param fileName 原始文件名
     * @param deviceNo 设备编号（用于生成唯一文件名）
     * @return 保存后的文件路径（相对路径）
     * @throws IOException 文件保存异常
     */
    private String saveFileToLocal(MultipartFile file, String fileName, String deviceNo) throws IOException {
        // 确保上传目录存在
        Path uploadDir = Paths.get(uploadPath);
        if (!Files.exists(uploadDir)) {
            Files.createDirectories(uploadDir);
            log.info("创建上传目录: {}", uploadDir.toAbsolutePath());
        }
        
        // 生成唯一文件名：设备编号_时间戳_原始文件名
        String fileExtension = getFileExtension(fileName);
        String baseFileName = fileName;
        if (baseFileName.contains(".")) {
            baseFileName = baseFileName.substring(0, baseFileName.lastIndexOf('.'));
        }
        String uniqueFileName = String.format("%s_%d_%s.%s", 
                deviceNo, 
                System.currentTimeMillis(), 
                baseFileName,
                fileExtension);
        
        // 保存文件
        Path filePath = uploadDir.resolve(uniqueFileName);
        Files.copy(file.getInputStream(), filePath);
        
        // 返回相对路径
        return uploadPath + File.separator + uniqueFileName;
    }
    
    /**
     * 分片信息内部类
     */
    private static class ChunkInfo {
        char prefix;
        int chunkNumber;
        boolean isLastChunk;
        int totalChunks;
        
        ChunkInfo(char prefix, int chunkNumber, boolean isLastChunk, int totalChunks) {
            this.prefix = prefix;
            this.chunkNumber = chunkNumber;
            this.isLastChunk = isLastChunk;
            this.totalChunks = totalChunks;
        }
    }
    
    /**
     * 响应数据内部类
     */
    public static class ResponseData {
        public Integer code;
        public Integer status;
        public Boolean success;
        public String message;
        public Long timestamp;
    }
}

