package services

import (
	"context"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

// VideoService 视频处理服务
type VideoService struct {
	mu sync.Mutex
}

// NewVideoService 创建视频服务实例
func NewVideoService() *VideoService {
	return &VideoService{}
}

// FolderInfo 文件夹信息
type FolderInfo struct {
	Path       string   `json:"path"`
	Name       string   `json:"name"`
	VideoCount int      `json:"videoCount"`
	VideoPaths []string `json:"videoPaths"`
}

// VideoConcatRequest 视频拼接请求
type VideoConcatRequest struct {
	FolderPath     string       `json:"folderPath"`
	Num            int          `json:"num"`
	JoinType       int          `json:"joinType"` // 1: 组合拼接, 2: 顺序拼接
	AuditImagePath string       `json:"auditImagePath"`
	FolderInfos    []FolderInfo `json:"folderInfos"`
}

// VideoConcatResult 视频拼接结果
type VideoConcatResult struct {
	Index    int    `json:"index"`
	FileName string `json:"fileName"`
	FilePath string `json:"filePath"`
	Size     string `json:"size"`
	Seconds  int    `json:"seconds"`
	Status   string `json:"status"`
	Progress string `json:"progress"`
}

// ListFolders 列出文件夹中的视频文件夹
func (s *VideoService) ListFolders(ctx context.Context, folderPath string) ([]FolderInfo, error) {
	var folderInfos []FolderInfo

	dir, err := os.ReadDir(folderPath)
	if err != nil {
		return nil, fmt.Errorf("读取目录失败: %v", err)
	}

	for _, entry := range dir {
		if !entry.IsDir() || entry.Name() == "output" {
			continue
		}

		fullPath := filepath.Join(folderPath, entry.Name())
		videoFiles, err := filepath.Glob(filepath.Join(fullPath, "*.mp4"))
		if err != nil {
			continue
		}

		folderInfos = append(folderInfos, FolderInfo{
			Path:       fullPath,
			Name:       entry.Name(),
			VideoCount: len(videoFiles),
			VideoPaths: videoFiles,
		})
	}

	return folderInfos, nil
}

// GetLargeFileMD5 计算大文件的 MD5
func (s *VideoService) GetLargeFileMD5(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer file.Close()

	hash := md5.New()
	buffer := make([]byte, 8192) // 8KB 缓冲区

	for {
		n, err := file.Read(buffer)
		if n > 0 {
			hash.Write(buffer[:n])
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			return "", err
		}
	}

	return hex.EncodeToString(hash.Sum(nil)), nil
}

// ConvertVideoToTS 将视频转换为 TS 格式
func (s *VideoService) ConvertVideoToTS(videoPath string) (string, error) {
	// 计算 MD5
	md5Hash, err := s.GetLargeFileMD5(videoPath)
	if err != nil {
		return "", fmt.Errorf("计算MD5失败: %v", err)
	}

	// 临时文件路径
	tempDir := os.TempDir()
	tsPath := filepath.Join(tempDir, md5Hash+".ts")

	// 如果文件已存在，直接返回
	if _, err := os.Stat(tsPath); err == nil {
		return tsPath, nil
	}

	// 使用 FFmpeg 转换
	helper := GetFFmpegHelper()
	if !helper.IsAvailable() {
		return "", fmt.Errorf("ffmpeg 不可用，请确保已安装 ffmpeg")
	}
	cmd := helper.Command("-i", videoPath,
		"-c:v", "libx264",
		"-c:a", "aac",
		"-ar", "44100",
		"-b:a", "128k",
		"-crf", "23",
		"-vf", "fps=30",
		"-movflags", "+faststart",
		"-f", "mpegts",
		"-y", tsPath)

	output, err := cmd.CombinedOutput()
	if err != nil {
		// 尝试备用方案
		cmd2 := helper.Command("-i", videoPath,
			"-c", "copy",
			"-f", "mpegts",
			"-y", tsPath)
		output2, err2 := cmd2.CombinedOutput()
		if err2 != nil {
			return "", fmt.Errorf("视频转换失败: %v, 输出: %s, 备用方案失败: %v, 输出: %s", err, string(output), err2, string(output2))
		}
		// 备用方案成功，继续执行
	}

	return tsPath, nil
}

// GenerateCombinations 生成所有视频组合（笛卡尔积）
func (s *VideoService) GenerateCombinations(videoLists [][]string, index int, currentCombination []string, result *[][]string) {
	if index == len(videoLists) {
		combination := make([]string, len(currentCombination))
		copy(combination, currentCombination)
		*result = append(*result, combination)
		return
	}

	for _, video := range videoLists[index] {
		currentCombination = append(currentCombination, video)
		s.GenerateCombinations(videoLists, index+1, currentCombination, result)
		currentCombination = currentCombination[:len(currentCombination)-1]
	}
}

// JoinVideos 拼接视频
// 注意：Wails3 中回调函数需要使用事件系统，这里先简化处理
func (s *VideoService) JoinVideos(ctx context.Context, req VideoConcatRequest) ([]VideoConcatResult, error) {
	var results []VideoConcatResult
	// 创建输出目录
	outputDir := filepath.Join(req.FolderPath, "output")
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return nil, fmt.Errorf("创建输出目录失败: %v", err)
	}

	// 准备视频列表
	var videoLists [][]string
	for _, folderInfo := range req.FolderInfos {
		videoLists = append(videoLists, folderInfo.VideoPaths)
	}

	var combinations [][]string

	if req.JoinType == 1 {
		// 组合拼接模式
		s.GenerateCombinations(videoLists, 0, []string{}, &combinations)

		// 随机选择指定数量的组合
		if req.Num < len(combinations) {
			rand.Seed(time.Now().UnixNano())
			selected := make(map[int]bool)
			newCombinations := [][]string{}
			for len(newCombinations) < req.Num {
				idx := rand.Intn(len(combinations))
				if !selected[idx] {
					selected[idx] = true
					newCombinations = append(newCombinations, combinations[idx])
				}
			}
			combinations = newCombinations
		}
	} else if req.JoinType == 2 {
		// 顺序拼接模式
		if len(videoLists) == 0 {
			return nil, fmt.Errorf("没有视频文件")
		}
		count := len(videoLists[0])
		for i := 1; i < len(videoLists); i++ {
			if len(videoLists[i]) != count {
				return nil, fmt.Errorf("所有文件夹中的视频数量必须相同")
			}
		}

		for i := 0; i < count; i++ {
			combination := []string{}
			for _, videoList := range videoLists {
				combination = append(combination, videoList[i])
			}
			combinations = append(combinations, combination)
		}
	}

	// 先转换所有视频为 TS 格式
	var allVideoPaths []string
	for _, combination := range combinations {
		allVideoPaths = append(allVideoPaths, combination...)
	}

	// 去重
	uniqueVideos := make(map[string]bool)
	var uniqueVideoPaths []string
	for _, path := range allVideoPaths {
		if !uniqueVideos[path] {
			uniqueVideos[path] = true
			uniqueVideoPaths = append(uniqueVideoPaths, path)
		}
	}

	// 并发转换视频（限制并发数）
	semaphore := make(chan struct{}, 10)
	var wg sync.WaitGroup
	tsMap := make(map[string]string)
	var mu sync.Mutex

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

			tsPath, err := s.ConvertVideoToTS(path)
			if err != nil {
				LogError(fmt.Sprintf("转换视频失败: %v", err))
				return
			}

			mu.Lock()
			tsMap[path] = tsPath
			mu.Unlock()
		}(videoPath)
	}
	wg.Wait()

	// 拼接视频
	semaphore = make(chan struct{}, 10)
	for i, combination := range combinations {
		wg.Add(1)
		go func(index int, combo []string) {
			defer wg.Done()
			semaphore <- struct{}{}
			defer func() { <-semaphore }()

			// 获取第一个视频的文件名
			firstVideoName := strings.TrimSuffix(filepath.Base(combo[0]), filepath.Ext(combo[0]))
			outputFileName := fmt.Sprintf("%s_%04d.mp4", firstVideoName, index+1)
			outputPath := filepath.Join(outputDir, outputFileName)

			// 创建结果对象
			result := VideoConcatResult{
				Index:    index + 1,
				FileName: outputFileName,
				FilePath: outputPath,
				Status:   "处理中",
				Progress: "0%",
			}

			// 获取 TS 文件路径
			var tsPaths []string
			for _, videoPath := range combo {
				if tsPath, ok := tsMap[videoPath]; ok {
					tsPaths = append(tsPaths, tsPath)
				} else {
					LogError(fmt.Sprintf("找不到视频的TS文件: %s", videoPath))
					result.Status = "拼接失败"
					result.Progress = "失败"
					mu.Lock()
					results = append(results, result)
					mu.Unlock()
					return
				}
			}

			// 创建 concat 文件
			concatFile := filepath.Join(os.TempDir(), fmt.Sprintf("concat_%d_%d.txt", time.Now().Unix(), index))
			file, err := os.Create(concatFile)
			if err != nil {
				LogError(fmt.Sprintf("创建concat文件失败: %v", err))
				result.Status = "拼接失败"
				result.Progress = "失败"
				mu.Lock()
				results = append(results, result)
				mu.Unlock()
				return
			}

			for _, tsPath := range tsPaths {
				file.WriteString(fmt.Sprintf("file '%s'\n", strings.ReplaceAll(tsPath, "\\", "/")))
			}
			file.Close()
			defer os.Remove(concatFile)

			// 拼接视频
			args := []string{
				"-f", "concat",
				"-safe", "0",
				"-i", concatFile,
				"-c:v", "copy",
				"-c:a", "aac",
				"-ar", "44100",
				"-movflags", "+faststart",
				"-analyzeduration", "100M",
				"-probesize", "100M",
				"-y", outputPath,
			}

			helper := GetFFmpegHelper()
			if !helper.IsAvailable() {
				LogError("ffmpeg 不可用")
				result.Status = "拼接失败"
				result.Progress = "失败"
				mu.Lock()
				results = append(results, result)
				mu.Unlock()
				return
			}
			cmd := helper.Command(args...)
			err = cmd.Run()
			if err != nil {
				LogError(fmt.Sprintf("拼接视频失败: %v", err))
				result.Status = "拼接失败"
				result.Progress = "失败"
				mu.Lock()
				results = append(results, result)
				mu.Unlock()
				return
			}

			// 如果指定了审核图片，添加水印
			if req.AuditImagePath != "" {
				outputImgPath := filepath.Join(outputDir, fmt.Sprintf("%s_%04d_img.mp4", firstVideoName, index+1))
				args := []string{
					"-i", outputPath,
					"-i", req.AuditImagePath,
					"-filter_complex", "[0:v][1:v] overlay=0:H-h",
					"-movflags", "+faststart",
					"-y", outputImgPath,
				}

				helper := GetFFmpegHelper()
				if !helper.IsAvailable() {
					LogError("ffmpeg 不可用")
					result.Status = "拼接失败"
					result.Progress = "失败"
					mu.Lock()
					results = append(results, result)
					mu.Unlock()
					return
				}
				cmd := helper.Command(args...)
				cmd.Run() // 忽略错误，水印是可选的
			}

			// 获取文件信息
			fileInfo, err := os.Stat(outputPath)
			if err != nil {
				result.Status = "拼接失败"
				result.Progress = "失败"
				mu.Lock()
				results = append(results, result)
				mu.Unlock()
				return
			}

			// 获取视频时长（使用 ffprobe）
			seconds := 0
			if helper.IsProbeAvailable() {
				cmd = helper.ProbeCommand("-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", outputPath)
			} else {
				cmd = nil
			}
			if cmd != nil {
				output, err := cmd.Output()
				if err == nil {
					var duration float64
					fmt.Sscanf(strings.TrimSpace(string(output)), "%f", &duration)
					seconds = int(duration)
				}
			}

			sizeMB := fileInfo.Size() / 1024 / 1024

			result.FilePath = outputPath
			result.Size = fmt.Sprintf("%dMB", sizeMB)
			result.Seconds = seconds
			result.Status = "拼接成功"
			result.Progress = "100%"

			mu.Lock()
			results = append(results, result)
			mu.Unlock()
		}(i, combination)
	}

	wg.Wait()
	return results, nil
}
