package services

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"os/exec"
	"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)
	}
	return matches, nil
}

// RemoveFrameRandom 随机删除视频中的一帧
func (s *ExtractService) RemoveFrameRandom(ctx context.Context, inputPath string, outputPath string) error {
	// 创建临时目录
	tempDir := filepath.Join(os.TempDir(), fmt.Sprintf("extract_%d", time.Now().UnixNano()))
	defer os.RemoveAll(tempDir)
	os.MkdirAll(tempDir, 0755)

	// 获取视频信息
	cmd := exec.Command("ffprobe", "-v", "error", "-show_entries", "format=duration:stream=codec_name,r_frame_rate", "-of", "default=noprint_wrappers=1:nokey=1", inputPath)
	output, err := cmd.Output()
	if err != nil {
		return fmt.Errorf("获取视频信息失败: %v", err)
	}

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

	for i, line := range lines {
		line = strings.TrimSpace(line)
		if i == 0 && line != "" {
			fmt.Sscanf(line, "%f", &duration)
		}
		if strings.HasPrefix(line, "hevc") || strings.HasPrefix(line, "h264") {
			codecName = line
		}
		if strings.Contains(line, "/") {
			parts := strings.Split(line, "/")
			if len(parts) == 2 {
				var num, den float64
				fmt.Sscanf(parts[0], "%f", &num)
				fmt.Sscanf(parts[1], "%f", &den)
				if den > 0 {
					frameRate = num / den
					_ = frameRate // 避免未使用变量警告
				}
			}
		}
	}

	// 检查时长
	if duration < 20 {
		return fmt.Errorf("视频时长太短（%.2f秒），无法抽帧（需要至少20秒）", duration)
	}

	// 如果是 HEVC，先转换为 H.264
	if codecName == "hevc" {
		videoConvert := filepath.Join(tempDir, "convert.mp4")
		cmd := exec.Command("ffmpeg", "-i", inputPath, "-c:v", "libx264", "-y", videoConvert)
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("转换HEVC失败: %v", err)
		}
		inputPath = videoConvert
	}

	// 随机选择要删除的帧时间点（避开开头和结尾）
	rand.Seed(time.Now().UnixNano())
	minFrameTime := 20.0
	maxFrameTime := duration - 5.0
	if maxFrameTime <= minFrameTime {
		maxFrameTime = minFrameTime + 1.0
	}
	randomFrame := minFrameTime + rand.Float64()*(maxFrameTime-minFrameTime)

	// 分割视频
	videoPart1 := filepath.Join(tempDir, "part1.mp4")
	videoPart2 := filepath.Join(tempDir, "part2.mp4")

	// 第一部分：0 到 randomFrame - 0.016
	cmd = exec.Command("ffmpeg", "-i", inputPath, "-ss", "0", "-t", fmt.Sprintf("%.6f", randomFrame-0.016), "-c", "copy", "-y", videoPart1)
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("裁剪第一部分失败: %v", err)
	}

	// 第二部分：randomFrame 到结束
	cmd = exec.Command("ffmpeg", "-i", inputPath, "-ss", fmt.Sprintf("%.6f", randomFrame), "-c", "copy", "-y", videoPart2)
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("裁剪第二部分失败: %v", err)
	}

	// 合并视频
	concatFile := filepath.Join(tempDir, "concat.txt")
	file, err := os.Create(concatFile)
	if err != nil {
		return fmt.Errorf("创建concat文件失败: %v", err)
	}
	file.WriteString(fmt.Sprintf("file '%s'\n", strings.ReplaceAll(videoPart1, "\\", "/")))
	file.WriteString(fmt.Sprintf("file '%s'\n", strings.ReplaceAll(videoPart2, "\\", "/")))
	file.Close()

	cmd = exec.Command("ffmpeg", "-f", "concat", "-safe", "0", "-i", concatFile, "-c", "copy", "-y", outputPath)
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("合并视频失败: %v", err)
	}

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

	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)
	}

	// 生成任务列表
	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 {
			result = ExtractFrameResult{
				VideoPath: t.VideoPath,
				Success:   false,
				Error:     err.Error(),
			}
		} else {
			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 {
	comment := fmt.Sprintf("JSY_%s", time.Now().Format("20060102150405"))
	cmd := exec.Command("ffmpeg", "-i", inputPath,
		"-c", "copy",
		"-metadata", fmt.Sprintf("comment=%s", comment),
		"-y", outputPath)

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("修改元数据失败: %v", err)
	}

	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)
	}

	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 }()

			rand.Seed(time.Now().UnixNano())
			randomNum := rand.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 {
			result = ExtractFrameResult{
				VideoPath: videoPath,
				Success:   false,
				Error:     err.Error(),
			}
		} else {
			result = ExtractFrameResult{
				VideoPath:  videoPath,
				OutputPath: outputPath,
				Success:    true,
			}
		}
		results = append(results, result)
		mu.Unlock()
		}(video)
	}

	wg.Wait()
	return results, nil
}

