package services

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"veza-backend-api/internal/models"

	"github.com/google/uuid"

	"go.uber.org/zap"
)

// HLSTranscodeService gère le transcodage HLS des tracks audio
type HLSTranscodeService struct {
	outputDir string
	bitrates  []int
	logger    *zap.Logger
}

// NewHLSTranscodeService crée un nouveau service de transcodage HLS
func NewHLSTranscodeService(outputDir string, logger *zap.Logger) *HLSTranscodeService {
	if logger == nil {
		logger = zap.NewNop()
	}
	return &HLSTranscodeService{
		outputDir: outputDir,
		bitrates:  []int{128, 192, 320},
		logger:    logger,
	}
}

// SetBitrates configure les bitrates à utiliser pour le transcodage
func (s *HLSTranscodeService) SetBitrates(bitrates []int) {
	s.bitrates = bitrates
}

// TranscodeTrack transcodage un track en format HLS avec plusieurs qualités
func (s *HLSTranscodeService) TranscodeTrack(ctx context.Context, track *models.Track) (*models.HLSStream, error) {
	if track == nil {
		return nil, fmt.Errorf("track cannot be nil")
	}

	if track.FilePath == "" {
		return nil, fmt.Errorf("track file path is empty")
	}

	// Vérifier que le fichier source existe
	if _, err := os.Stat(track.FilePath); os.IsNotExist(err) {
		return nil, fmt.Errorf("track file does not exist: %s", track.FilePath)
	}

	trackDir := filepath.Join(s.outputDir, fmt.Sprintf("track_%s", track.ID))
	if err := os.MkdirAll(trackDir, 0755); err != nil {
		return nil, fmt.Errorf("failed to create track directory: %w", err)
	}

	// Cleanup en cas d'erreur
	var cleanupErr error
	defer func() {
		if cleanupErr != nil {
			// Nettoyer en cas d'erreur
			if err := s.cleanupTrackDir(trackDir); err != nil {
				s.logger.Error("Failed to cleanup track directory", zap.Error(err))
			}
		}
	}()

	var bitrates []int
	for _, bitrate := range s.bitrates {
		if err := s.transcodeBitrate(ctx, track, trackDir, bitrate); err != nil {
			cleanupErr = err
			return nil, fmt.Errorf("failed to transcode bitrate %dk: %w", bitrate, err)
		}
		bitrates = append(bitrates, bitrate)
		s.logger.Info("Transcoded bitrate", zap.Int("bitrate", bitrate), zap.String("track_id", track.ID.String()))
	}

	playlistURL := filepath.Join(trackDir, "master.m3u8")
	if err := s.generateMasterPlaylist(trackDir, bitrates); err != nil {
		cleanupErr = err
		return nil, fmt.Errorf("failed to generate master playlist: %w", err)
	}

	segmentsCount, err := s.countSegments(trackDir)
	if err != nil {
		cleanupErr = err
		return nil, fmt.Errorf("failed to count segments: %w", err)
	}

	return &models.HLSStream{
			TrackID:       track.ID,
			PlaylistURL:   playlistURL,
			SegmentsCount: segmentsCount,
			Bitrates:      models.BitrateList(bitrates),
			Status:        models.HLSStatusReady,
		},
		nil
}

// transcodeBitrate transcodage un track pour un bitrate spécifique
func (s *HLSTranscodeService) transcodeBitrate(ctx context.Context, track *models.Track, outputDir string, bitrate int) error {
	qualityDir := filepath.Join(outputDir, fmt.Sprintf("%dk", bitrate))
	if err := os.MkdirAll(qualityDir, 0755); err != nil {
		return fmt.Errorf("failed to create quality directory: %w", err)
	}

	outputPattern := filepath.Join(qualityDir, "segment_%03d.ts")
	playlistPath := filepath.Join(qualityDir, "playlist.m3u8")

	// Commande ffmpeg pour transcoder en HLS
	cmd := exec.CommandContext(ctx, "ffmpeg",
		"-i", track.FilePath,
		"-codec:a", "aac",
		"-b:a", fmt.Sprintf("%dk", bitrate),
		"-hls_time", "10",
		"-hls_playlist_type", "vod",
		"-hls_segment_filename", outputPattern,
		"-hls_list_size", "0", // Inclure tous les segments
		"-y", // Overwrite output files
		playlistPath,
	)

	// Capturer la sortie pour le logging
	output, err := cmd.CombinedOutput()
	if err != nil {
		s.logger.Error("FFmpeg transcoding failed",
			zap.Int("bitrate", bitrate),
			zap.String("track_id", track.ID.String()),
			zap.String("output", string(output)),
			zap.Error(err))
		return fmt.Errorf("ffmpeg failed: %w", err)
	}

	// Vérifier que le fichier playlist a été créé
	if _, err := os.Stat(playlistPath); os.IsNotExist(err) {
		return fmt.Errorf("playlist file was not created: %s", playlistPath)
	}

	return nil
}

// generateMasterPlaylist génère le fichier master.m3u8 avec toutes les qualités
func (s *HLSTranscodeService) generateMasterPlaylist(trackDir string, bitrates []int) error {
	masterPlaylistPath := filepath.Join(trackDir, "master.m3u8")

	var lines []string
	lines = append(lines, "#EXTM3U")
	lines = append(lines, "#EXT-X-VERSION:3")

	for _, bitrate := range bitrates {
		qualityDir := fmt.Sprintf("%dk", bitrate)
		playlistPath := filepath.Join(qualityDir, "playlist.m3u8")

		// Ajouter l'entrée pour cette qualité
		lines = append(lines, fmt.Sprintf("#EXT-X-STREAM-INF:BANDWIDTH=%d000", bitrate))
		lines = append(lines, playlistPath)
	}

	content := strings.Join(lines, "\n") + "\n"

	if err := os.WriteFile(masterPlaylistPath, []byte(content), 0644); err != nil {
		return fmt.Errorf("failed to write master playlist: %w", err)
	}

	return nil
}

// getPlaylistDuration lit la durée totale d'une playlist .m3u8
func (s *HLSTranscodeService) getPlaylistDuration(playlistPath string) float64 {
	data, err := os.ReadFile(playlistPath)
	if err != nil {
		return 0
	}

	lines := strings.Split(string(data), "\n")
	var totalDuration float64

	for _, line := range lines {
		if strings.HasPrefix(line, "#EXTINF:") {
			// Format: #EXTINF:10.0,
			parts := strings.Split(line, ":")
			if len(parts) > 1 {
				durationStr := strings.TrimSuffix(parts[1], ",")
				var duration float64
				if _, err := fmt.Sscanf(durationStr, "%f", &duration); err == nil {
					totalDuration += duration
				}
			}
		}
	}

	return totalDuration
}

// countSegments compte le nombre de segments .ts dans le répertoire du track
// T0344: Compte les segments dans chaque répertoire de qualité et retourne le maximum
func (s *HLSTranscodeService) countSegments(trackDir string) (int, error) {
	// Check if track directory exists
	if _, err := os.Stat(trackDir); os.IsNotExist(err) {
		return 0, fmt.Errorf("track directory does not exist: %s", trackDir)
	}

	count := 0
	for _, bitrate := range s.bitrates {
		qualityDir := filepath.Join(trackDir, fmt.Sprintf("%dk", bitrate))
		files, err := filepath.Glob(filepath.Join(qualityDir, "segment_*.ts"))
		if err != nil {
			return 0, fmt.Errorf("failed to glob segments in %s: %w", qualityDir, err)
		}
		if len(files) > count {
			count = len(files)
		}
	}
	return count, nil
}

// cleanupTrackDir supprime le répertoire d'un track en cas d'erreur
func (s *HLSTranscodeService) cleanupTrackDir(trackDir string) error {
	return os.RemoveAll(trackDir)
}

// CleanupTrackDir supprime le répertoire d'un track (méthode publique)
func (s *HLSTranscodeService) CleanupTrackDir(trackID uuid.UUID) error {
	trackDir := filepath.Join(s.outputDir, fmt.Sprintf("track_%s", trackID))
	return s.cleanupTrackDir(trackDir)
}
