package services

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

// ExtractService 抽帧服务
type ExtractService struct {
	mu sync.Mutex
}

// NewExtractService 创建抽帧服务实例
func NewExtractService() *ExtractService {
	return &ExtractService{}
}

// ExtractFrameRequest 抽帧请求
type ExtractFrameRequest struct {
	FolderPath   string `json:"folderPath"`
	ExtractCount int    `json:"extractCount"` // 每个视频生成的数量
}

// ExtractFrameResult 抽帧结果
type ExtractFrameResult struct {
	VideoPath  string `json:"videoPath"`
	OutputPath string `json:"outputPath"`
	Success    bool   `json:"success"`
	Error      string `json:"error,omitempty"`
}

// ListVideos 列出文件夹中的视频文件
func (s *ExtractService) ListVideos(ctx context.Context, folderPath string) ([]string, error) {
	// 只查找当前目录下的视频文件，排除子目录（特别是输出目录）
	pattern := filepath.Join(folderPath, "*.mp4")
	matches, err := filepath.Glob(pattern)
	if err != nil {
		return nil, fmt.Errorf("查找视频文件失败: %v", err)
	}

	// 过滤掉输出目录中的文件
	var filteredMatches []string
	outputDir := filepath.Join(folderPath, "out")
	outputDirAlt := filepath.Join(folderPath, "output") // 兼容 output 目录

	for _, match := range matches {
		// 检查文件是否在输出目录中
		absMatch, err := filepath.Abs(match)
		if err != nil {
			continue
		}
		absOutputDir, _ := filepath.Abs(outputDir)
		absOutputDirAlt, _ := filepath.Abs(outputDirAlt)

		// 如果文件不在输出目录中，则添加
		if !strings.HasPrefix(absMatch, absOutputDir+string(filepath.Separator)) &&
			!strings.HasPrefix(absMatch, absOutputDirAlt+string(filepath.Separator)) {
			filteredMatches = append(filteredMatches, match)
		}
	}

	LogDebugf("找到 %d 个视频文件（已排除输出目录中的 %d 个文件）", len(filteredMatches), len(matches)-len(filteredMatches))
	return filteredMatches, nil
}

// RemoveFrameRandom 随机删除视频中的一帧
func (s *ExtractService) RemoveFrameRandom(ctx context.Context, inputPath string, outputPath string) error {
	LogDebugf("开始抽帧处理: %s -> %s", inputPath, outputPath)

	// 检查输入文件是否存在
	if _, err := os.Stat(inputPath); err != nil {
		return fmt.Errorf("输入文件不存在: %v", err)
	}

	// 创建临时目录
	tempDir := filepath.Join(os.TempDir(), fmt.Sprintf("extract_%d", time.Now().UnixNano()))
	defer os.RemoveAll(tempDir)
	if err := os.MkdirAll(tempDir, 0755); err != nil {
		return fmt.Errorf("创建临时目录失败: %v", err)
	}
	LogDebugf("临时目录已创建: %s", tempDir)

	// 获取视频信息
	helper := GetFFmpegHelper()
	if !helper.IsProbeAvailable() {
		return fmt.Errorf("ffprobe 不可用，请确保已安装 ffmpeg")
	}

	// 使用更详细的 ffprobe 命令获取视频信息
	cmd := helper.ProbeCommand("-v", "error", "-show_entries",
		"format=duration", "-show_entries", "stream=codec_name,r_frame_rate",
		"-of", "default=noprint_wrappers=1:nokey=1", inputPath)
	if cmd == nil {
		return fmt.Errorf("无法创建 ffprobe 命令")
	}
	output, err := cmd.Output()
	if err != nil {
		// 如果上面的命令失败，尝试更简单的方式
		LogWarnf("获取视频信息失败，尝试备用方法: %v, 输出: %s", err, string(output))
		cmd = helper.ProbeCommand("-v", "error", "-show_format", "-show_streams", "-of", "json", inputPath)
		if cmd == nil {
			return fmt.Errorf("无法创建 ffprobe 命令")
		}
		output2, err2 := cmd.Output()
		if err2 != nil {
			return fmt.Errorf("获取视频信息失败: %v, 输出: %s", err2, string(output2))
		}
		// 如果备用方法也失败，返回错误
		return fmt.Errorf("无法解析视频信息，可能文件已损坏或格式不支持。输出: %s", string(output2))
	}

	lines := strings.Split(strings.TrimSpace(string(output)), "\n")
	var duration float64
	var codecName string
	var frameRate float64 = 30.0 // 默认帧率

	// 解析 ffprobe 输出
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}

		// 尝试解析时长（可能是第一个数值行）
		if duration == 0 {
			var d float64
			if n, _ := fmt.Sscanf(line, "%f", &d); n == 1 && d > 0 {
				duration = d
				LogDebugf("解析到视频时长: %.2f 秒", duration)
			}
		}

		// 查找编解码器
		if strings.HasPrefix(line, "hevc") || strings.HasPrefix(line, "h264") {
			codecName = line
			LogDebugf("检测到编解码器: %s", codecName)
		}

		// 解析帧率
		if strings.Contains(line, "/") {
			parts := strings.Split(line, "/")
			if len(parts) == 2 {
				var num, den float64
				if n, _ := fmt.Sscanf(parts[0], "%f", &num); n == 1 {
					if n, _ := fmt.Sscanf(parts[1], "%f", &den); n == 1 && den > 0 {
						frameRate = num / den
						LogDebugf("解析到帧率: %.2f fps", frameRate)
					}
				}
			}
		}
	}

	// 检查时长
	if duration <= 0 {
		LogErrorf("无法获取视频时长，ffprobe 输出: %s", string(output))
		return fmt.Errorf("无法获取视频时长（%.2f秒），文件可能已损坏或格式不支持", duration)
	}
	if duration < 20 {
		return fmt.Errorf("视频时长太短（%.2f秒），无法抽帧（需要至少20秒）", duration)
	}

	LogDebugf("视频信息: 时长=%.2f秒, 编解码器=%s, 帧率=%.2f", duration, codecName, frameRate)

	// 如果是 HEVC，先转换为 H.264
	if codecName == "hevc" {
		if !helper.IsAvailable() {
			return fmt.Errorf("ffmpeg 不可用，请确保已安装 ffmpeg")
		}
		videoConvert := filepath.Join(tempDir, "convert.mp4")
		cmd := helper.Command("-i", inputPath, "-c:v", "libx264", "-y", videoConvert)
		if cmd == nil {
			return fmt.Errorf("无法创建 ffmpeg 命令")
		}
		output, err := cmd.CombinedOutput()
		if err != nil {
			return fmt.Errorf("转换HEVC失败: %v, 输出: %s", err, string(output))
		}
		inputPath = videoConvert
	}

	// 获取绝对路径（Windows 兼容）
	absInputPath, err := filepath.Abs(inputPath)
	if err != nil {
		absInputPath = inputPath
	}

	// 计算要删除的帧编号和时间范围
	// 计算总帧数
	totalFrames := int(duration * frameRate)
	if totalFrames <= 0 {
		totalFrames = int(duration * 30) // 默认30fps
	}

	// 随机选择要删除的帧（避开开头和结尾）
	source := rand.NewSource(time.Now().UnixNano())
	r := rand.New(source)
	minFrame := int(frameRate * 20)                // 至少20秒后的帧
	maxFrame := totalFrames - int(frameRate*5) - 1 // 至少5秒前的帧
	if maxFrame <= minFrame {
		maxFrame = minFrame + 100 // 确保有足够的帧可选
	}
	randomFrameNum := minFrame + r.Intn(maxFrame-minFrame+1)

	// 计算删除的时间范围：删除一帧（约0.016秒）
	frameDuration := 1.0 / frameRate
	deleteStartTime := float64(randomFrameNum) * frameDuration
	deleteEndTime := deleteStartTime + frameDuration

	LogDebugf("删除帧: 帧编号=%d, 时间范围=%.6f~%.6f秒, 总时长=%.2f秒",
		randomFrameNum, deleteStartTime, deleteEndTime, duration)

	// 使用 filter_complex 精确删除指定时间段
	// 方法：使用 trim 和 concat filter 来删除指定时间段
	// 视频部分：第一部分(0到deleteStartTime) + 第二部分(deleteEndTime到结束)
	// 音频部分：第一部分(0到deleteStartTime) + 第二部分(deleteEndTime到结束)
	filterComplex := fmt.Sprintf(
		"[0:v]trim=start=0:end=%.6f,setpts=PTS-STARTPTS[v1];[0:v]trim=start=%.6f:end=%.6f,setpts=PTS-STARTPTS[v2];[v1][v2]concat=n=2:v=1:a=0[outv];[0:a]atrim=start=0:end=%.6f,asetpts=PTS-STARTPTS[a1];[0:a]atrim=start=%.6f:end=%.6f,asetpts=PTS-STARTPTS[a2];[a1][a2]concat=n=2:v=0:a=1[outa]",
		deleteStartTime, deleteEndTime, duration, deleteStartTime, deleteEndTime, duration)

	// 使用 filter_complex 一次性处理，确保时长精确
	cmd = helper.Command("-i", absInputPath,
		"-filter_complex", filterComplex,
		"-map", "[outv]", "-map", "[outa]",
		"-c:v", "libx264", "-preset", "veryfast", "-crf", "23",
		"-g", "30", "-keyint_min", "30", // 确保关键帧间隔，避免卡顿
		"-c:a", "aac", "-b:a", "128k",
		"-movflags", "+faststart", "-y", outputPath)
	if cmd == nil {
		return fmt.Errorf("无法创建 ffmpeg 命令")
	}
	output, err = cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("删除帧失败: %v, 输出: %s", err, string(output))
	}

	// 验证输出视频时长
	if helper.IsProbeAvailable() {
		cmd = helper.ProbeCommand("-v", "error", "-show_entries", "format=duration",
			"-of", "default=noprint_wrappers=1:nokey=1", outputPath)
		if cmd != nil {
			if probeOutput, err := cmd.Output(); err == nil {
				var finalDuration float64
				if n, _ := fmt.Sscanf(strings.TrimSpace(string(probeOutput)), "%f", &finalDuration); n == 1 {
					expectedDuration := duration - frameDuration // 减去删除的一帧时间
					diff := finalDuration - expectedDuration
					if diff < -0.1 || diff > 0.1 { // 允许0.1秒的误差
						LogWarnf("视频时长偏差较大: 期望约%.2f秒, 实际%.2f秒, 偏差%.2f秒",
							expectedDuration, finalDuration, diff)
					} else {
						LogDebugf("视频时长验证: 实际%.2f秒 (期望约%.2f秒, 偏差%.3f秒)",
							finalDuration, expectedDuration, diff)
					}
				}
			}
		}
	}

	// 验证输出文件
	fileInfo, err := os.Stat(outputPath)
	if err != nil {
		return fmt.Errorf("输出文件不存在: %v", err)
	}
	if fileInfo.Size() == 0 {
		return fmt.Errorf("输出文件大小为0，可能写入失败")
	}

	LogInfof("抽帧完成: %s (大小: %d 字节)", outputPath, fileInfo.Size())
	return nil
}

// ExtractFrames 批量抽帧
func (s *ExtractService) ExtractFrames(ctx context.Context, req ExtractFrameRequest) ([]ExtractFrameResult, error) {
	var results []ExtractFrameResult
	// 列出所有视频
	videos, err := s.ListVideos(ctx, req.FolderPath)
	if err != nil {
		return nil, err
	}

	if len(videos) == 0 {
		return nil, fmt.Errorf("没有找到视频文件")
	}

	// 创建输出目录
	outputDir := filepath.Join(req.FolderPath, "out")
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return nil, fmt.Errorf("创建输出目录失败: %v", err)
	}
	LogDebugf("输出目录已创建: %s", outputDir)

	// 生成任务列表
	type Task struct {
		VideoPath  string
		Index      int
		OutputPath string
	}

	var tasks []Task
	for _, video := range videos {
		originalFileName := strings.TrimSuffix(filepath.Base(video), filepath.Ext(video))
		extension := filepath.Ext(video)
		for i := 1; i <= req.ExtractCount; i++ {
			outputFileName := fmt.Sprintf("%s_%04d%s", originalFileName, i, extension)
			outputPath := filepath.Join(outputDir, outputFileName)
			tasks = append(tasks, Task{
				VideoPath:  video,
				Index:      i,
				OutputPath: outputPath,
			})
		}
	}

	// 并发处理（限制并发数）
	semaphore := make(chan struct{}, 10)
	var wg sync.WaitGroup
	// total := len(tasks) // 未使用
	current := 0
	var mu sync.Mutex

	for _, task := range tasks {
		wg.Add(1)
		go func(t Task) {
			defer wg.Done()
			semaphore <- struct{}{}
			defer func() { <-semaphore }()

			// 检查文件是否已存在
			if _, err := os.Stat(t.OutputPath); err == nil {
				mu.Lock()
				current++
				result := ExtractFrameResult{
					VideoPath:  t.VideoPath,
					OutputPath: t.OutputPath,
					Success:    true,
				}
				results = append(results, result)
				mu.Unlock()
				return
			}

			err := s.RemoveFrameRandom(ctx, t.VideoPath, t.OutputPath)
			mu.Lock()
			current++
			var result ExtractFrameResult
			if err != nil {
				LogErrorf("抽帧失败 [%d/%d]: %s, 错误: %v", current, len(tasks), t.VideoPath, err)
				result = ExtractFrameResult{
					VideoPath: t.VideoPath,
					Success:   false,
					Error:     err.Error(),
				}
			} else {
				LogDebugf("抽帧成功 [%d/%d]: %s -> %s", current, len(tasks), t.VideoPath, t.OutputPath)
				result = ExtractFrameResult{
					VideoPath:  t.VideoPath,
					OutputPath: t.OutputPath,
					Success:    true,
				}
			}
			results = append(results, result)
			mu.Unlock()
		}(task)
	}

	wg.Wait()
	return results, nil
}

// ModifyByMetadata 通过修改元数据改变文件 MD5
func (s *ExtractService) ModifyByMetadata(ctx context.Context, inputPath string, outputPath string) error {
	helper := GetFFmpegHelper()
	if !helper.IsAvailable() {
		return fmt.Errorf("ffmpeg 不可用，请确保已安装 ffmpeg")
	}

	// 获取绝对路径
	absInputPath, err := filepath.Abs(inputPath)
	if err != nil {
		absInputPath = inputPath
	}

	comment := fmt.Sprintf("JSY_%s", time.Now().Format("20060102150405"))

	// 首先尝试使用 -c copy（流复制），这样可以快速处理且不重新编码
	cmd := helper.Command("-i", absInputPath,
		"-c", "copy",
		"-metadata", fmt.Sprintf("comment=%s", comment),
		"-y", outputPath)

	output, err := cmd.CombinedOutput()
	if err != nil {
		LogWarnf("使用流复制修改元数据失败，尝试重新编码: %v, 输出: %s", err, string(output))

		// 如果流复制失败，尝试重新编码（使用高质量编码）
		cmd = helper.Command("-i", absInputPath,
			"-c:v", "libx264",
			"-preset", "veryfast",
			"-crf", "18", // 高质量编码
			"-c:a", "copy", // 音频流复制
			"-metadata", fmt.Sprintf("comment=%s", comment),
			"-movflags", "+faststart",
			"-y", outputPath)

		output, err = cmd.CombinedOutput()
		if err != nil {
			return fmt.Errorf("修改元数据失败: %v, 输出: %s", err, string(output))
		}
	}

	// 验证输出文件是否存在
	if _, err := os.Stat(outputPath); err != nil {
		return fmt.Errorf("输出文件不存在: %v", err)
	}

	// 验证文件大小（确保文件已正确写入）
	fileInfo, err := os.Stat(outputPath)
	if err != nil {
		return fmt.Errorf("无法验证输出文件: %v", err)
	}
	if fileInfo.Size() == 0 {
		return fmt.Errorf("输出文件大小为0，可能写入失败")
	}

	return nil
}

// ModifyVideosMetadata 批量修改视频元数据
func (s *ExtractService) ModifyVideosMetadata(ctx context.Context, folderPath string) ([]ExtractFrameResult, error) {
	var results []ExtractFrameResult
	videos, err := s.ListVideos(ctx, folderPath)
	if err != nil {
		return nil, err
	}

	outputDir := filepath.Join(folderPath, "out")
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return nil, fmt.Errorf("创建输出目录失败: %v", err)
	}
	LogDebugf("输出目录已创建: %s", outputDir)

	semaphore := make(chan struct{}, 10)
	var wg sync.WaitGroup
	// total := len(videos) // 未使用
	current := 0
	var mu sync.Mutex

	for _, video := range videos {
		wg.Add(1)
		go func(videoPath string) {
			defer wg.Done()
			semaphore <- struct{}{}
			defer func() { <-semaphore }()

			// 使用纳秒时间戳作为随机种子，确保每个 goroutine 都有不同的随机数
			source := rand.NewSource(time.Now().UnixNano())
			r := rand.New(source)
			randomNum := r.Intn(90000) + 10000
			outputFileName := fmt.Sprintf("modify%d%s", randomNum, filepath.Base(videoPath))
			outputPath := filepath.Join(outputDir, outputFileName)

			err := s.ModifyByMetadata(ctx, videoPath, outputPath)
			mu.Lock()
			current++
			var result ExtractFrameResult
			if err != nil {
				LogErrorf("修改元数据失败 [%d/%d]: %s, 错误: %v", current, len(videos), videoPath, err)
				result = ExtractFrameResult{
					VideoPath: videoPath,
					Success:   false,
					Error:     err.Error(),
				}
			} else {
				LogDebugf("修改元数据成功 [%d/%d]: %s -> %s", current, len(videos), videoPath, outputPath)
				result = ExtractFrameResult{
					VideoPath:  videoPath,
					OutputPath: outputPath,
					Success:    true,
				}
			}
			results = append(results, result)
			mu.Unlock()
		}(video)
	}

	wg.Wait()
	return results, nil
}
