package backend

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
)

type FlacInfo struct {
	Path          string `json:"path"`
	SampleRate    uint32 `json:"sample_rate"`
	BitsPerSample uint8  `json:"bits_per_sample"`
}

func GetFlacInfoBatch(paths []string) []FlacInfo {
	results := make([]FlacInfo, len(paths))
	var wg sync.WaitGroup

	for i, path := range paths {
		wg.Add(1)
		go func(idx int, p string) {
			defer wg.Done()
			info := FlacInfo{Path: p}

			ffprobePath, err := GetFFprobePath()
			if err != nil {
				results[idx] = info
				return
			}

			args := []string{
				"-v", "error",
				"-select_streams", "a:0",
				"-show_entries", "stream=sample_rate,bits_per_raw_sample,bits_per_sample",
				"-of", "default=noprint_wrappers=0",
				p,
			}
			cmd := exec.Command(ffprobePath, args...)
			setHideWindow(cmd)
			out, err := cmd.CombinedOutput()
			if err != nil {
				results[idx] = info
				return
			}

			kvMap := make(map[string]string)
			for _, line := range strings.Split(string(out), "\n") {
				if parts := strings.SplitN(line, "=", 2); len(parts) == 2 {
					kvMap[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
				}
			}

			if v, ok := kvMap["sample_rate"]; ok {
				if s, err := strconv.Atoi(v); err == nil {
					info.SampleRate = uint32(s)
				}
			}

			bits := 0
			if v, ok := kvMap["bits_per_raw_sample"]; ok && v != "N/A" && v != "" {
				bits, _ = strconv.Atoi(v)
			}
			if bits == 0 {
				if v, ok := kvMap["bits_per_sample"]; ok && v != "N/A" && v != "" {
					bits, _ = strconv.Atoi(v)
				}
			}
			info.BitsPerSample = uint8(bits)

			results[idx] = info
		}(i, path)
	}

	wg.Wait()
	return results
}

type ResampleRequest struct {
	InputFiles []string `json:"input_files"`
	SampleRate string   `json:"sample_rate"`
	BitDepth   string   `json:"bit_depth"`
}

type ResampleResult struct {
	InputFile  string `json:"input_file"`
	OutputFile string `json:"output_file"`
	Success    bool   `json:"success"`
	Error      string `json:"error,omitempty"`
}

func buildFolderLabel(sampleRate, bitDepth string) string {
	var parts []string

	if bitDepth != "" {
		parts = append(parts, bitDepth+"bit")
	}

	switch sampleRate {
	case "44100":
		parts = append(parts, "44.1kHz")
	case "48000":
		parts = append(parts, "48kHz")
	case "96000":
		parts = append(parts, "96kHz")
	case "192000":
		parts = append(parts, "192kHz")
	default:
		if sampleRate != "" {
			parts = append(parts, sampleRate+"Hz")
		}
	}

	if len(parts) == 0 {
		return "Resampled"
	}
	return strings.Join(parts, " ")
}

func ResampleAudio(req ResampleRequest) ([]ResampleResult, error) {
	ffmpegPath, err := GetFFmpegPath()
	if err != nil {
		return nil, fmt.Errorf("failed to get ffmpeg path: %w", err)
	}

	if err := ValidateExecutable(ffmpegPath); err != nil {
		return nil, fmt.Errorf("invalid ffmpeg executable: %w", err)
	}

	installed, err := IsFFmpegInstalled()
	if err != nil || !installed {
		return nil, fmt.Errorf("ffmpeg is not installed")
	}

	if req.SampleRate == "" && req.BitDepth == "" {
		return nil, fmt.Errorf("at least one of sample rate or bit depth must be specified")
	}

	results := make([]ResampleResult, len(req.InputFiles))
	var wg sync.WaitGroup
	var mu sync.Mutex

	folderLabel := buildFolderLabel(req.SampleRate, req.BitDepth)

	for i, inputFile := range req.InputFiles {
		wg.Add(1)
		go func(idx int, inputFile string) {
			defer wg.Done()

			result := ResampleResult{
				InputFile: inputFile,
			}

			inputExt := strings.ToLower(filepath.Ext(inputFile))
			baseName := strings.TrimSuffix(filepath.Base(inputFile), inputExt)
			inputDir := filepath.Dir(inputFile)

			outputDir := filepath.Join(inputDir, folderLabel)
			if err := os.MkdirAll(outputDir, 0755); err != nil {
				result.Error = fmt.Sprintf("failed to create output directory: %v", err)
				result.Success = false
				mu.Lock()
				results[idx] = result
				mu.Unlock()
				return
			}

			outputFile := filepath.Join(outputDir, baseName+".flac")
			result.OutputFile = outputFile

			args := []string{
				"-i", inputFile,
				"-y",
			}

			if req.BitDepth != "" {
				switch req.BitDepth {
				case "16":
					args = append(args, "-c:a", "flac", "-sample_fmt", "s16")
				case "24":
					args = append(args, "-c:a", "flac", "-sample_fmt", "s32", "-bits_per_raw_sample", "24")
				default:
					args = append(args, "-c:a", "flac")
				}
			} else {
				args = append(args, "-c:a", "flac")
			}

			if req.SampleRate != "" {
				args = append(args, "-ar", req.SampleRate)
			}

			args = append(args, "-map_metadata", "0")
			args = append(args, outputFile)

			fmt.Printf("[Resample] %s -> %s\n", inputFile, outputFile)

			cmd := exec.Command(ffmpegPath, args...)
			setHideWindow(cmd)
			output, err := cmd.CombinedOutput()
			if err != nil {
				result.Error = fmt.Sprintf("resampling failed: %s - %s", err.Error(), string(output))
				result.Success = false
				mu.Lock()
				results[idx] = result
				mu.Unlock()
				return
			}

			result.Success = true
			fmt.Printf("[Resample] Done: %s\n", outputFile)
			mu.Lock()
			results[idx] = result
			mu.Unlock()
		}(i, inputFile)
	}

	wg.Wait()
	return results, nil
}
