package media

import (
	"bytes"
	"context"
	"errors"
	"log/slog"
	"os"
	"os/exec"
	"strings"
)

func IsIOSPlayableMime(mime string) bool {
	switch mime {
	case "video/mp4", "video/quicktime", "audio/mp4", "audio/aac", "audio/x-m4a", "audio/mpeg":
		return true
	}
	return false
}

func IsAudio(mime string) bool {
	return strings.HasPrefix(mime, "audio/")
}

type TranscodeInput struct {
	SourceURL string
	MimeType  string
}

type TranscodeOutput struct {
	TempLocalFilePath string
	OutputMimeType    string
	OutputExt         string // e.g. ".m4a" or ".mp4"
}

var (
	ErrInvalidInput error = errors.New("invalid input")
)

// TranscodeToMp4 writes the result to a temp file; caller is responsible
// for deleting TempLocalFilePath.
func TranscodeToMp4(ctx context.Context, input TranscodeInput) (*TranscodeOutput, error) {
	if input.SourceURL == "" || input.MimeType == "" {
		return nil, ErrInvalidInput
	}

	isAudio := IsAudio(input.MimeType)
	var outputExt, outputMime string
	if isAudio {
		outputExt = ".m4a"
		outputMime = "audio/mp4"
	} else {
		outputExt = ".mp4"
		outputMime = "video/mp4"
	}

	tmp, err := os.CreateTemp("", "transcode-*"+outputExt)
	if err != nil {
		slog.Error("transcode: failed to create temp file", "error", err)
		return nil, err
	}
	tmpPath := tmp.Name()
	tmp.Close()

	var args []string
	if isAudio {
		args = []string{
			"-y", "-i", input.SourceURL,
			"-vn",
			"-c:a", "aac", "-b:a", "128k",
			"-movflags", "+faststart",
			tmpPath,
		}
	} else {
		// 1440p–4K screen recordings + libx264's lookahead buffers can OOM the
		// worker. Bound parallelism/lookahead and downscale to 1080p; the
		// original WebM stays in GCS untouched.
		args = []string{
			"-y", "-i", input.SourceURL,
			"-vf", "scale='min(1920,iw)':-2:flags=lanczos",
			"-c:v", "libx264", "-preset", "veryfast", "-crf", "23",
			"-pix_fmt", "yuv420p", "-profile:v", "baseline", "-level", "3.1",
			"-x264-params", "rc-lookahead=20:ref=2",
			"-threads", "2", "-filter_threads", "2",
			"-c:a", "aac", "-b:a", "128k",
			"-movflags", "+faststart",
			tmpPath,
		}
	}

	cmd := exec.CommandContext(ctx, "ffmpeg", args...)
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		return nil, errors.Join(err, errors.New(stderr.String()))
	}

	return &TranscodeOutput{
		TempLocalFilePath: tmpPath,
		OutputMimeType:    outputMime,
		OutputExt:         outputExt,
	}, nil
}
