package ffmpeg

import (
	"errors"
	"fmt"
)

// only support H264 by default, since Safari does not support VP8/VP9
var defaultSupportedCodecs = []string{H264, H265}

var validForH264Mkv = []Container{Mp4, Matroska}
var validForH264 = []Container{Mp4}
var validForH265Mkv = []Container{Mp4, Matroska}
var validForH265 = []Container{Mp4}
var validForVp8 = []Container{Webm}
var validForVp9Mkv = []Container{Webm, Matroska}
var validForVp9 = []Container{Webm}
var validForHevcMkv = []Container{Mp4, Matroska}
var validForHevc = []Container{Mp4}

var validAudioForMkv = []ProbeAudioCodec{Aac, Mp3, Vorbis, Opus}
var validAudioForWebm = []ProbeAudioCodec{Vorbis, Opus}
var validAudioForMp4 = []ProbeAudioCodec{Aac, Mp3}

var (
	// ErrUnsupportedVideoCodecForBrowser is returned when the video codec is not supported for browser streaming.
	ErrUnsupportedVideoCodecForBrowser = errors.New("unsupported video codec for browser")

	// ErrUnsupportedVideoCodecContainer is returned when the video codec/container combination is not supported for browser streaming.
	ErrUnsupportedVideoCodecContainer = errors.New("video codec/container combination is unsupported for browser streaming")

	// ErrUnsupportedAudioCodecContainer is returned when the audio codec/container combination is not supported for browser streaming.
	ErrUnsupportedAudioCodecContainer = errors.New("audio codec/container combination is unsupported for browser streaming")
)

// IsStreamable returns nil if the file is streamable, or an error if it is not.
func IsStreamable(videoCodec string, audioCodec ProbeAudioCodec, container Container) error {
	supportedVideoCodecs := defaultSupportedCodecs

	// check if the video codec matches the supported codecs
	if !isValidCodec(videoCodec, supportedVideoCodecs) {
		return fmt.Errorf("%w: %s", ErrUnsupportedVideoCodecForBrowser, videoCodec)
	}

	if !isValidCombo(videoCodec, container, supportedVideoCodecs) {
		return fmt.Errorf("%w: %s/%s", ErrUnsupportedVideoCodecContainer, videoCodec, container)
	}

	if !IsValidAudioForContainer(audioCodec, container) {
		return fmt.Errorf("%w: %s/%s", ErrUnsupportedAudioCodecContainer, audioCodec, container)
	}

	return nil
}

func isValidCodec(codecName string, supportedCodecs []string) bool {
	for _, c := range supportedCodecs {
		if c == codecName {
			return true
		}
	}
	return false
}

func isValidAudio(audio ProbeAudioCodec, validCodecs []ProbeAudioCodec) bool {
	// if audio codec is missing or unsupported by ffmpeg we can't do anything about it
	// report it as valid so that the file can at least be streamed directly if the video codec is supported
	if audio == MissingUnsupported {
		return true
	}

	for _, c := range validCodecs {
		if c == audio {
			return true
		}
	}

	return false
}

// IsValidAudioForContainer returns true if the audio codec is valid for the container.
func IsValidAudioForContainer(audio ProbeAudioCodec, format Container) bool {
	switch format {
	case Matroska:
		return isValidAudio(audio, validAudioForMkv)
	case Webm:
		return isValidAudio(audio, validAudioForWebm)
	case Mp4:
		return isValidAudio(audio, validAudioForMp4)
	}
	return false
}

// isValidCombo checks if a codec/container combination is valid.
// Returns true on validity, false otherwise
func isValidCombo(codecName string, format Container, supportedVideoCodecs []string) bool {
	supportMKV := isValidCodec(Mkv, supportedVideoCodecs)
	supportHEVC := isValidCodec(Hevc, supportedVideoCodecs)

	switch codecName {
	case H264:
		if supportMKV {
			return isValidForContainer(format, validForH264Mkv)
		}
		return isValidForContainer(format, validForH264)
	case H265:
		if supportMKV {
			return isValidForContainer(format, validForH265Mkv)
		}
		return isValidForContainer(format, validForH265)
	case Vp8:
		return isValidForContainer(format, validForVp8)
	case Vp9:
		if supportMKV {
			return isValidForContainer(format, validForVp9Mkv)
		}
		return isValidForContainer(format, validForVp9)
	case Hevc:
		if supportHEVC {
			if supportMKV {
				return isValidForContainer(format, validForHevcMkv)
			}
			return isValidForContainer(format, validForHevc)
		}
	}
	return false
}

func isValidForContainer(format Container, validContainers []Container) bool {
	for _, fmt := range validContainers {
		if fmt == format {
			return true
		}
	}
	return false
}
