package runner

import (
	"context"
	"encoding/json"
	"fmt"
	"math"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

const generatedVideoMetadataProbeTimeout = 8 * time.Second

type generatedVideoMetadata struct {
	Duration      float64
	HasAudio      bool
	HasAudioKnown bool
}

type ffprobeVideoMetadata struct {
	Format struct {
		Duration string `json:"duration"`
	} `json:"format"`
	Streams []struct {
		CodecType string `json:"codec_type"`
	} `json:"streams"`
}

func (s *Service) enrichGeneratedVideoMetadata(ctx context.Context, taskKind string, result map[string]any) map[string]any {
	if taskKind != "videos.generations" {
		return result
	}
	data, _ := result["data"].([]any)
	if len(data) == 0 {
		return result
	}
	for _, raw := range data {
		item, _ := raw.(map[string]any)
		if len(item) == 0 || !isGeneratedVideoItem(item) {
			continue
		}
		needsDuration := floatFromAny(item["duration"]) <= 0
		_, hasAudioMetadata := boolishOptional(firstPresentValue(item, "has_audio", "hasAudio"))
		if !needsDuration && hasAudioMetadata {
			continue
		}
		urlValue := firstNonEmptyStringValue(item, "video_url", "videoUrl", "url")
		if urlValue == "" {
			continue
		}
		metadata, err := s.probeVideoMetadata(ctx, urlValue)
		if err != nil {
			if s.logger != nil {
				s.logger.Debug("probe generated video metadata failed", "url", trimForLog(urlValue), "error", err)
			}
			continue
		}
		if needsDuration && metadata.Duration > 0 {
			item["duration"] = metadata.Duration
		}
		if !hasAudioMetadata && metadata.HasAudioKnown {
			item["has_audio"] = metadata.HasAudio
		}
	}
	return result
}

func isGeneratedVideoItem(item map[string]any) bool {
	itemType := strings.TrimSpace(stringFromAny(item["type"]))
	if itemType == "video" {
		return true
	}
	if firstNonEmptyStringValue(item, "video_url", "videoUrl") != "" {
		return true
	}
	urlValue := strings.ToLower(firstNonEmptyStringValue(item, "url"))
	return strings.Contains(urlValue, ".mp4") ||
		strings.Contains(urlValue, ".mov") ||
		strings.Contains(urlValue, ".webm") ||
		strings.Contains(urlValue, ".m3u8")
}

func (s *Service) probeVideoMetadata(ctx context.Context, rawURL string) (generatedVideoMetadata, error) {
	if _, err := exec.LookPath("ffprobe"); err != nil {
		return generatedVideoMetadata{}, err
	}
	probeURL := rawURL
	if s != nil {
		if resolved, err := s.generatedAssetFetchURL(rawURL); err == nil && strings.TrimSpace(resolved) != "" {
			probeURL = resolved
		}
	}
	probeCtx, cancel := context.WithTimeout(ctx, generatedVideoMetadataProbeTimeout)
	defer cancel()
	cmd := exec.CommandContext(
		probeCtx,
		"ffprobe",
		"-v", "error",
		"-show_entries", "format=duration:stream=codec_type",
		"-of", "json",
		probeURL,
	)
	output, err := cmd.Output()
	if err != nil {
		return generatedVideoMetadata{}, err
	}
	var probed ffprobeVideoMetadata
	if err := json.Unmarshal(output, &probed); err != nil {
		return generatedVideoMetadata{}, err
	}
	metadata := generatedVideoMetadata{}
	if durationText := strings.TrimSpace(probed.Format.Duration); durationText != "" {
		if duration, err := strconv.ParseFloat(durationText, 64); err == nil && duration > 0 && !math.IsNaN(duration) && !math.IsInf(duration, 0) {
			rounded := math.Round(duration)
			if rounded <= 0 {
				rounded = 1
			}
			metadata.Duration = rounded
		}
	}
	if probed.Streams != nil {
		metadata.HasAudioKnown = true
		for _, stream := range probed.Streams {
			if strings.TrimSpace(stream.CodecType) == "audio" {
				metadata.HasAudio = true
				break
			}
		}
	}
	if metadata.Duration <= 0 && !metadata.HasAudioKnown {
		return metadata, fmt.Errorf("invalid video metadata: %q", trimForLog(string(output)))
	}
	return metadata, nil
}

func trimForLog(value string) string {
	value = strings.TrimSpace(value)
	if len(value) <= 120 {
		return value
	}
	return value[:120] + "..."
}
