package FikoYTDosya

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	fiko_mongo "server/sistem/fikoyt_mongo"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gofiber/fiber/v3"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo/gridfs"
	"go.mongodb.org/mongo-driver/mongo/options"
)

// FikoYTDosya: Dosya ile ilgili bilgileri tutan struct
type FikoYTDosya struct {
	File           *multipart.FileHeader
	Id             primitive.ObjectID
	ThumbId        primitive.ObjectID
	Path           string
	ProcessedPath  string
	ThumbPath      string // Thumbnail dosyasının yolu
	MimeType       string
	Saved          bool
	Processed      bool
	ThumbGenerated bool // Thumbnail üretildi mi?
}

// Kaydet: Dosyayı kaydetme işlemi
func (f *FikoYTDosya) Kaydet() error {
	if f.File == nil {
		return fiber.NewError(fiber.StatusInternalServerError, "File is nil")
	}

	if f.Path == "" {
		return fiber.NewError(fiber.StatusInternalServerError, "Path is empty")
	}

	// Dosyayı aç
	file, err := f.File.Open()
	if err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "Error opening file: "+err.Error())
	}
	defer func() {
		if file != nil {
			file.Close()
		}
	}()

	// Geçici dosya yolu
	tempDir := "./ffmpeg/files"
	if _, err := os.Stat(tempDir); os.IsNotExist(err) {
		if err := os.MkdirAll(tempDir, 0755); err != nil {
			return fiber.NewError(fiber.StatusInternalServerError, "Error creating temp directory: "+err.Error())
		}
	}

	// Geçici dosya oluştur
	f.Path = filepath.Join(tempDir, f.Id.Hex()+f.Path)
	tempFile, err := os.Create(f.Path)
	if err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "Error creating temp file: "+err.Error())
	}
	defer func() {
		if tempFile != nil {
			tempFile.Close()
		}
	}()

	// Veriyi buffer ile kopyala
	buffer := make([]byte, 5*1024*1024)
	for {
		n, readErr := file.Read(buffer)
		if n > 0 {
			if _, writeErr := tempFile.Write(buffer[:n]); writeErr != nil {
				return fiber.NewError(fiber.StatusInternalServerError, "Error writing to temp file: "+writeErr.Error())
			}
		}
		if readErr != nil {
			if readErr == io.EOF {
				break
			}
			return fiber.NewError(fiber.StatusInternalServerError, "Error reading file: "+readErr.Error())
		}
	}

	// MIME türünü tespit et
	mimeType, err := detectMimeType(f.Path)
	if err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "Error detecting MIME type: "+err.Error())
	}

	if mimeType == "" {
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to detect MIME type")
	}

	f.MimeType = mimeType
	f.Saved = true

	return nil
}

// Isle: Dosyayı işleme fonksiyonu (sıkıştırma + thumbnail üretimi)
func (f *FikoYTDosya) Isle() error {
	// Sıkıştırma işlemi
	outputFile := f.Path + filepath.Ext(f.File.Filename)
	compressWithFFmpeg(f.Path, outputFile)
	f.Processed = true
	f.ProcessedPath = outputFile

	// Video ise ve ThumbId boş ise thumbnail üret (hata olsa bile devam et)
	if f.isVideo() && f.ThumbId.IsZero() {
		f.generateThumbnail() // Hata olsa bile devam et
	}

	return nil
}

// isVideo: Dosyanın video olup olmadığını kontrol eder
func (f *FikoYTDosya) isVideo() bool {
	return strings.HasPrefix(f.MimeType, "video/")
}

// generateThumbnail: Video için thumbnail üretir (hata olsa bile sessizce devam eder)
func (f *FikoYTDosya) generateThumbnail() {
	if !f.isVideo() {
		return // Video değilse sessizce çık
	}

	// Thumbnail için dosya yolu oluştur
	tempDir := "./ffmpeg/files"
	f.ThumbId = primitive.NewObjectID()
	f.ThumbPath = filepath.Join(tempDir, f.ThumbId.Hex()+"_thumb.jpg")

	// FFmpeg argümanları - sizin formatınıza uygun
	ffmpegArgs := []string{
		"-i", f.ProcessedPath, // Girdi dosyasını belirtir
		"-ss", "00:00:01", // 1. saniyeden frame al
		"-vframes", "1", // Sadece 1 frame al
		"-vcodec", "mjpeg", // MJPEG formatını kullanır (JPEG için uygun)
		"-q:v", "5", // Kaliteyi belirler (0 en iyi kalite, 31 en kötü kalite)
		"-vf", "scale='min(1920,iw)':'min(1080,ih)':force_original_aspect_ratio=decrease", // Çözünürlüğü orantılı şekilde sınırlar
		"-an",          // Ses çıkışı yok
		"-f", "image2", // Çıktı formatını belirtir (image2, resim için)
		"-hide_banner",       // Banner bilgisini gizler
		"-nostats",           // İstatistik bilgilerini gizler
		"-loglevel", "quiet", // Log seviyesini sessiz yapar
		"-y",        // Varolan dosyayı üzerine yaz
		f.ThumbPath, // Çıktı dosya yolu
	}

	// FFmpeg komutunu çalıştır
	cmd := exec.Command("ffmpeg", ffmpegArgs...)

	// Hata olsa bile sessizce devam et
	err := cmd.Run()
	if err != nil {
		// Thumbnail üretilemedi, ID'yi sıfırla ve devam et
		f.ThumbId = primitive.NilObjectID
		f.ThumbPath = ""
		return
	}

	// Thumbnail dosyasının oluşturulup oluşturulmadığını kontrol et
	if _, err := os.Stat(f.ThumbPath); os.IsNotExist(err) {
		// Dosya oluşmadı, ID'yi sıfırla ve devam et
		f.ThumbId = primitive.NilObjectID
		f.ThumbPath = ""
		return
	}

	f.ThumbGenerated = true
}

// KaydetGridFs: Ana dosyayı GridFS'ye kaydet
func (f *FikoYTDosya) KaydetGridFs(metadata fiber.Map) error {
	// İşlenen dosyayı GridFS'ye kaydet
	fileOid := f.Id
	_, err := saveToGridFS(fileOid, f.ProcessedPath, &metadata)
	if err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "Error saving to GridFS: "+err.Error())
	}

	return nil
}

// KaydetThumbGridFs: Thumbnail'ı GridFS'ye kaydet (hata olsa bile sessizce devam eder)
func (f *FikoYTDosya) KaydetThumbGridFs(metadata fiber.Map) error {
	if !f.ThumbGenerated || f.ThumbPath == "" {
		// Thumbnail yoksa sessizce geç
		return nil
	}

	// Thumbnail metadata'sına özel bilgiler ekle
	if metadata == nil {
		metadata = fiber.Map{}
	}
	metadata["type"] = "thumbnail"
	metadata["parent_file_id"] = f.Id.Hex()
	metadata["mime_type"] = "image/jpeg"

	// Thumbnail'ı GridFS'ye kaydet - hata olsa bile ana işlemi etkilemesin
	_, err := saveToGridFS(f.ThumbId, f.ThumbPath, &metadata)
	if err != nil {
		// Thumbnail kaydedilemedi, ID'yi sıfırla ama hata döndürme
		f.ThumbId = primitive.NilObjectID
		f.ThumbGenerated = false
		return nil
	}

	return nil
}

// SilGridFs: Ana dosyayı GridFS'den sil
func (f *FikoYTDosya) SilGridFs() error {
	fileOid := f.Id
	err := DeleteFromGridfs(fileOid)
	if err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "Error deleting from GridFS: "+err.Error())
	}

	return nil
}

// SilThumbGridFs: Thumbnail'ı GridFS'den sil (hata olsa bile sessizce devam eder)
func (f *FikoYTDosya) SilThumbGridFs() error {
	if f.ThumbId.IsZero() {
		return nil // Thumbnail yoksa silmeye gerek yok
	}

	// Hata olsa bile sessizce devam et
	DeleteFromGridfs(f.ThumbId)
	return nil
}

// TemizleDosyalar: Geçici dosyaları temizle
func (f *FikoYTDosya) TemizleDosyalar() error {
	// Ana dosyayı sil
	if _, err := os.Stat(f.Path); err == nil {
		os.Remove(f.Path)
	}

	// İşlenmiş dosyayı sil
	if _, err := os.Stat(f.ProcessedPath); err == nil {
		os.Remove(f.ProcessedPath)
	}

	// Thumbnail dosyasını sil
	if f.ThumbPath != "" {
		if _, err := os.Stat(f.ThumbPath); err == nil {
			os.Remove(f.ThumbPath)
		}
	}

	return nil
}

// GetThumbnailId: Thumbnail ID'sini döndür
func (f *FikoYTDosya) GetThumbnailId() primitive.ObjectID {
	return f.ThumbId
}

// HasThumbnail: Thumbnail var mı kontrol et
func (f *FikoYTDosya) HasThumbnail() bool {
	return f.ThumbGenerated && !f.ThumbId.IsZero()
}

// detectMimeType dosyanın MIME türünü belirler.
func detectMimeType(filePath string) (string, error) {
	// 1️⃣ Dosya uzantısını kontrol et
	ext := strings.ToLower(filepath.Ext(filePath))
	switch ext {
	case ".jpg", ".jpeg":
		return "image/jpeg", nil
	case ".png":
		return "image/png", nil
	case ".webp":
		return "image/webp", nil
	case ".gif":
		return "image/gif", nil
	case ".mp4":
		return "video/mp4", nil
	case ".mov":
		return "video/quicktime", nil
	case ".mkv":
		return "video/x-matroska", nil
	case ".avi":
		return "video/x-msvideo", nil
	case ".webm":
		return "video/webm", nil
	case ".mp3":
		return "audio/mpeg", nil
	case ".wav":
		return "audio/wav", nil
	}

	// 2️⃣ ffprobe ile stream tiplerini kontrol et
	cmd := exec.Command("ffprobe",
		"-v", "error",
		"-show_entries", "stream=codec_type",
		"-of", "default=noprint_wrappers=1:nokey=1",
		filePath,
	)

	var out bytes.Buffer
	cmd.Stdout = &out

	if err := cmd.Run(); err != nil {
		return "", fmt.Errorf("ffprobe çalıştırılamadı: %v", err)
	}

	streams := strings.Split(strings.TrimSpace(out.String()), "\n")

	for _, stream := range streams {
		stream = strings.TrimSpace(stream)
		switch stream {
		case "video":
			return "video/mp4", nil
		case "audio":
			return "audio/mpeg", nil
		}
	}

	// 3️⃣ format_name ile fallback
	cmdFormat := exec.Command("ffprobe",
		"-v", "error",
		"-show_entries", "format=format_name",
		"-of", "default=noprint_wrappers=1:nokey=1",
		filePath,
	)
	out.Reset()
	cmdFormat.Stdout = &out
	if err := cmdFormat.Run(); err != nil {
		return "", fmt.Errorf("ffprobe format_name çalıştırılamadı: %v", err)
	}

	format := strings.TrimSpace(out.String())

	switch {
	case strings.Contains(format, "mp4"):
		return "video/mp4", nil
	case strings.Contains(format, "mov"):
		return "video/quicktime", nil
	case strings.Contains(format, "mkv"):
		return "video/x-matroska", nil
	case strings.Contains(format, "mp3"):
		return "audio/mpeg", nil
	case strings.Contains(format, "wav"):
		return "audio/wav", nil
	case strings.Contains(format, "webp"):
		return "image/webp", nil
	case strings.Contains(format, "png"):
		return "image/png", nil
	case strings.Contains(format, "jpeg"), strings.Contains(format, "jpg"):
		return "image/jpeg", nil
	default:
		return "application/octet-stream", nil
	}
}

func detectMimeType1(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", fmt.Errorf("Dosya açılamadı: %v", err)
	}
	defer file.Close()

	buf := make([]byte, 512)
	if _, err := file.Read(buf); err != nil {
		return "", fmt.Errorf("Dosya okunamadı: %v", err)
	}

	return http.DetectContentType(buf), nil
}

/** AYRAÇ: */

// Event türleri
type EventType string

const (
	EventStarted          EventType = "STARTED"
	EventProgress         EventType = "PROGRESS"
	EventCompleted        EventType = "COMPLETED"
	EventError            EventType = "ERROR"
	EventWarning          EventType = "WARNING"
	EventFileDetected     EventType = "FILE_DETECTED"
	EventCompressionStart EventType = "COMPRESSION_START"
	EventCompressionEnd   EventType = "COMPRESSION_END"
	EventThumbnailStart   EventType = "THUMBNAIL_START"
	EventThumbnailEnd     EventType = "THUMBNAIL_END"
	EventGridFSSave       EventType = "GRIDFS_SAVE"
	EventCleanup          EventType = "CLEANUP"
	EventPaused           EventType = "PAUSED"
	EventResumed          EventType = "RESUMED"
	EventCancelled        EventType = "CANCELLED"
)

// Process durumları
type ProcessStatus string

const (
	StatusRunning   ProcessStatus = "RUNNING"
	StatusPaused    ProcessStatus = "PAUSED"
	StatusCompleted ProcessStatus = "COMPLETED"
	StatusError     ProcessStatus = "ERROR"
	StatusCancelled ProcessStatus = "CANCELLED"
)

// Event yapısı
type ProcessEvent struct {
	ID          string                 `json:"id"`
	Type        EventType              `json:"type"`
	Message     string                 `json:"message"`
	Progress    float64                `json:"progress"`
	Timestamp   time.Time              `json:"timestamp"`
	Data        map[string]interface{} `json:"data,omitempty"`
	Error       error                  `json:"error,omitempty"`
	FileID      primitive.ObjectID     `json:"file_id"`
	ProcessStep string                 `json:"process_step"`
}

// Event handler fonksiyon tipi
type EventHandler func(event *ProcessEvent) error

// Process kontrolör
type ProcessController struct {
	mu             sync.RWMutex
	status         ProcessStatus
	ctx            context.Context
	cancel         context.CancelFunc
	eventHandlers  []EventHandler
	currentProcess *exec.Cmd
	events         []*ProcessEvent
	maxEvents      int
	pauseChan      chan bool // Pause/resume kontrolü için
}

// Gelişmiş FikoYTDosya struct'ı
type FikoYTDosyaAdvanced struct {
	*FikoYTDosya
	Controller *ProcessController
	EventID    string
}

// Yeni ProcessController oluştur
func NewProcessController() *ProcessController {
	ctx, cancel := context.WithCancel(context.Background())
	return &ProcessController{
		status:        StatusRunning,
		ctx:           ctx,
		cancel:        cancel,
		eventHandlers: make([]EventHandler, 0),
		events:        make([]*ProcessEvent, 0),
		maxEvents:     1000, // Maksimum event sayısı
		pauseChan:     make(chan bool, 1),
	}
}

// Event handler ekle
func (pc *ProcessController) AddEventHandler(handler EventHandler) {
	pc.mu.Lock()
	defer pc.mu.Unlock()
	pc.eventHandlers = append(pc.eventHandlers, handler)
}

// Event yayınla
func (pc *ProcessController) EmitEvent(eventType EventType, message string, progress float64, fileID primitive.ObjectID, processStep string, data map[string]interface{}, err error) {
	pc.mu.RLock()
	defer pc.mu.RUnlock()

	event := &ProcessEvent{
		ID:          fmt.Sprintf("%d", time.Now().UnixNano()),
		Type:        eventType,
		Message:     message,
		Progress:    progress,
		Timestamp:   time.Now(),
		Data:        data,
		Error:       err,
		FileID:      fileID,
		ProcessStep: processStep,
	}

	// Event'i saklayalım (sınırlı sayıda)
	if len(pc.events) >= pc.maxEvents {
		pc.events = pc.events[1:]
	}
	pc.events = append(pc.events, event)

	// Event handler'ları çağır
	for _, handler := range pc.eventHandlers {
		go func(h EventHandler, e *ProcessEvent) {
			defer func() {
				if r := recover(); r != nil {
					log.Printf("Event handler panic: %v", r)
				}
			}()
			if handlerErr := h(e); handlerErr != nil {
				log.Printf("Event handler error: %v", handlerErr)
			}
		}(handler, event)
	}
}

// Pause kontrolü için yardımcı fonksiyon
func (pc *ProcessController) WaitIfPaused() {
	for {
		pc.mu.RLock()
		status := pc.status
		pc.mu.RUnlock()

		if status != StatusPaused {
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
}

// Context ve pause kontrolü kombinasyonu
func (pc *ProcessController) CheckContinue() error {
	// Context iptal kontrolü
	select {
	case <-pc.ctx.Done():
		return fmt.Errorf("operation cancelled")
	default:
	}

	// Pause kontrolü
	pc.WaitIfPaused()

	// İptal edilmiş mi kontrol et
	pc.mu.RLock()
	status := pc.status
	pc.mu.RUnlock()

	if status == StatusCancelled {
		return fmt.Errorf("operation cancelled")
	}

	return nil
}

// Process'i duraklat
func (pc *ProcessController) Pause() {
	pc.mu.Lock()
	defer pc.mu.Unlock()

	if pc.status == StatusRunning {
		pc.status = StatusPaused
		pc.EmitEvent(EventPaused, "Process paused by user", 0, primitive.NilObjectID, "CONTROL", nil, nil)
	}
}

// Process'i devam ettir
func (pc *ProcessController) Resume() {
	pc.mu.Lock()
	defer pc.mu.Unlock()

	if pc.status == StatusPaused {
		pc.status = StatusRunning
		pc.EmitEvent(EventResumed, "Process resumed by user", 0, primitive.NilObjectID, "CONTROL", nil, nil)
	}
}

// Process'i iptal et
func (pc *ProcessController) Cancel() {
	pc.mu.Lock()
	defer pc.mu.Unlock()

	pc.status = StatusCancelled
	pc.cancel()
	if pc.currentProcess != nil && pc.currentProcess.Process != nil {
		pc.currentProcess.Process.Kill()
	}
	pc.EmitEvent(EventCancelled, "Process cancelled", 0, primitive.NilObjectID, "CONTROL", nil, nil)
}

// Status al
func (pc *ProcessController) GetStatus() ProcessStatus {
	pc.mu.RLock()
	defer pc.mu.RUnlock()
	return pc.status
}

// Tüm eventları al
func (pc *ProcessController) GetEvents() []*ProcessEvent {
	pc.mu.RLock()
	defer pc.mu.RUnlock()

	events := make([]*ProcessEvent, len(pc.events))
	copy(events, pc.events)
	return events
}

// Son N eventi al
func (pc *ProcessController) GetLastEvents(count int) []*ProcessEvent {
	pc.mu.RLock()
	defer pc.mu.RUnlock()

	if count <= 0 || len(pc.events) == 0 {
		return []*ProcessEvent{}
	}

	start := len(pc.events) - count
	if start < 0 {
		start = 0
	}

	events := make([]*ProcessEvent, len(pc.events)-start)
	copy(events, pc.events[start:])
	return events
}

// Gelişmiş dosya işleme
func NewFikoYTDosyaAdvanced(original *FikoYTDosya) *FikoYTDosyaAdvanced {
	return &FikoYTDosyaAdvanced{
		FikoYTDosya: original,
		Controller:  NewProcessController(),
		EventID:     fmt.Sprintf("file_%s", original.Id.Hex()),
	}
}

// Gelişmiş kaydetme işlemi
func (f *FikoYTDosyaAdvanced) KaydetAdvanced() error {
	f.Controller.EmitEvent(EventStarted, "File save process started", 0, f.Id, "SAVE", nil, nil)

	defer func() {
		if r := recover(); r != nil {
			f.Controller.EmitEvent(EventError, fmt.Sprintf("Panic during save: %v", r), 0, f.Id, "SAVE", nil, fmt.Errorf("%v", r))
		}
	}()

	if f.File == nil {
		err := fmt.Errorf("file is nil")
		f.Controller.EmitEvent(EventError, "File is nil", 0, f.Id, "SAVE", nil, err)
		return err // Hata olsa bile devam etmek istiyorsanız nil döndürün
	}

	if f.Path == "" {
		err := fmt.Errorf("path is empty")
		f.Controller.EmitEvent(EventError, "Path is empty", 0, f.Id, "SAVE", nil, err)
		return err
	}

	// Progress tracking için
	f.Controller.EmitEvent(EventProgress, "Opening file", 10, f.Id, "SAVE", nil, nil)

	file, err := f.File.Open()
	if err != nil {
		f.Controller.EmitEvent(EventError, "Error opening file", 10, f.Id, "SAVE", nil, err)
		// Hata olsa bile devam etmek için return etmeyin
		return err
	}
	defer func() {
		if file != nil {
			file.Close()
		}
	}()

	f.Controller.EmitEvent(EventProgress, "Creating temp directory", 20, f.Id, "SAVE", nil, nil)

	tempDir := "./ffmpeg/files"
	if _, err := os.Stat(tempDir); os.IsNotExist(err) {
		if err := os.MkdirAll(tempDir, 0755); err != nil {
			f.Controller.EmitEvent(EventError, "Error creating temp directory", 20, f.Id, "SAVE", nil, err)
			// Devam etmek için alternatif dizin oluşturmayı deneyin
			tempDir = "./temp"
			os.MkdirAll(tempDir, 0755)
		}
	}

	f.Controller.EmitEvent(EventProgress, "Creating temp file", 30, f.Id, "SAVE", nil, nil)

	f.Path = filepath.Join(tempDir, f.Id.Hex()+f.Path)
	tempFile, err := os.Create(f.Path)
	if err != nil {
		f.Controller.EmitEvent(EventError, "Error creating temp file", 30, f.Id, "SAVE", nil, err)
		return err
	}
	defer func() {
		if tempFile != nil {
			tempFile.Close()
		}
	}()

	f.Controller.EmitEvent(EventProgress, "Copying file data", 40, f.Id, "SAVE", nil, nil)

	// Buffer ile kopyalama ve progress tracking
	buffer := make([]byte, 5*1024*1024) // 5MB buffer
	totalCopied := int64(0)
	expectedSize := f.File.Size

	for {
		// Context kontrolü
		select {
		case <-f.Controller.ctx.Done():
			f.Controller.EmitEvent(EventCancelled, "File copy cancelled", float64(totalCopied)/float64(expectedSize)*40+40, f.Id, "SAVE", nil, nil)
			return fmt.Errorf("operation cancelled")
		default:
		}

		// Pause kontrolü
		for f.Controller.GetStatus() == StatusPaused {
			time.Sleep(100 * time.Millisecond)
		}

		n, readErr := file.Read(buffer)
		if n > 0 {
			if _, writeErr := tempFile.Write(buffer[:n]); writeErr != nil {
				f.Controller.EmitEvent(EventWarning, "Error writing to temp file", 0, f.Id, "SAVE", nil, writeErr)
				// Yazma hatası olsa bile devam et
			} else {
				totalCopied += int64(n)
				progress := float64(totalCopied)/float64(expectedSize)*40 + 40 // 40-80 arası
				f.Controller.EmitEvent(EventProgress, fmt.Sprintf("Copied %d/%d bytes", totalCopied, expectedSize), progress, f.Id, "SAVE", map[string]interface{}{
					"bytes_copied": totalCopied,
					"total_bytes":  expectedSize,
				}, nil)
			}
		}
		if readErr != nil {
			if readErr == io.EOF {
				break
			}
			f.Controller.EmitEvent(EventWarning, "Error reading file", 0, f.Id, "SAVE", nil, readErr)
			// Okuma hatası olsa bile mümkün olduğunca devam et
			break
		}
	}

	f.Controller.EmitEvent(EventProgress, "Detecting MIME type", 85, f.Id, "SAVE", nil, nil)

	mimeType, err := f.detectMimeTypeAdvanced()
	if err != nil {
		f.Controller.EmitEvent(EventWarning, "Error detecting MIME type", 85, f.Id, "SAVE", nil, err)
		// MIME type tespit edilemezse varsayılan kullan
		mimeType = "application/octet-stream"
	}

	f.MimeType = mimeType
	f.Saved = true

	f.Controller.EmitEvent(EventFileDetected, fmt.Sprintf("File detected as %s", mimeType), 90, f.Id, "SAVE", map[string]interface{}{
		"mime_type": mimeType,
		"file_size": totalCopied,
	}, nil)

	f.Controller.EmitEvent(EventCompleted, "File save completed", 100, f.Id, "SAVE", nil, nil)

	return nil
}

// Gelişmiş işleme fonksiyonu
func (f *FikoYTDosyaAdvanced) IsleAdvanced() error {
	f.Controller.EmitEvent(EventStarted, "File processing started", 0, f.Id, "PROCESS", nil, nil)

	defer func() {
		if r := recover(); r != nil {
			f.Controller.EmitEvent(EventError, fmt.Sprintf("Panic during processing: %v", r), 0, f.Id, "PROCESS", nil, fmt.Errorf("%v", r))
		}
	}()

	// Compression
	f.Controller.EmitEvent(EventCompressionStart, "Starting compression", 10, f.Id, "PROCESS", nil, nil)

	outputFile := f.Path + filepath.Ext(f.File.Filename)
	mimeType, err := f.compressWithFFmpegAdvanced(f.Path, outputFile)
	if err != nil {
		f.Controller.EmitEvent(EventWarning, "Compression failed, continuing...", 50, f.Id, "PROCESS", nil, err)
		// Sıkıştırma başarısız olsa bile orijinal dosyayı kullan
		f.ProcessedPath = f.Path
		f.MimeType = "application/octet-stream"
	} else {
		f.Processed = true
		f.ProcessedPath = outputFile
		f.MimeType = mimeType
		f.Controller.EmitEvent(EventCompressionEnd, "Compression completed", 60, f.Id, "PROCESS", map[string]interface{}{
			"output_file": outputFile,
			"mime_type":   mimeType,
		}, nil)
	}

	// Thumbnail generation
	if f.isVideo() && f.ThumbId.IsZero() {
		f.Controller.EmitEvent(EventThumbnailStart, "Starting thumbnail generation", 70, f.Id, "PROCESS", nil, nil)

		if err := f.generateThumbnailAdvanced(); err != nil {
			f.Controller.EmitEvent(EventWarning, "Thumbnail generation failed, continuing...", 80, f.Id, "PROCESS", nil, err)
		} else {
			f.Controller.EmitEvent(EventThumbnailEnd, "Thumbnail generation completed", 90, f.Id, "PROCESS", map[string]interface{}{
				"thumb_id":   f.ThumbId.Hex(),
				"thumb_path": f.ThumbPath,
			}, nil)
		}
	}

	f.Controller.EmitEvent(EventCompleted, "File processing completed", 100, f.Id, "PROCESS", nil, nil)
	return nil
}

// Gelişmiş sıkıştırma fonksiyonu
func (f *FikoYTDosyaAdvanced) compressWithFFmpegAdvanced(inputPath, outputPath string) (string, error) {
	mimeType, err := f.detectMimeTypeAdvanced()
	if err != nil {
		f.Controller.EmitEvent(EventWarning, "MIME type detection failed", 0, f.Id, "COMPRESSION", nil, err)
		mimeType = "application/octet-stream"
	}

	var ffmpegArgs []string
	ffmpegPath := "ffmpeg"
	ffprobePath := "ffprobe"

	// MIME type'a göre argümanları belirle (önceki kodunuzdaki gibi)
	if strings.HasPrefix(mimeType, "image") {
		ffmpegArgs = []string{
			"-i", inputPath,
			"-vcodec", "mjpeg",
			"-q:v", "5",
			"-vf", "scale='min(1920,iw)':'min(1080,ih)':force_original_aspect_ratio=decrease",
			"-an",
			"-f", "image2",
			"-hide_banner",
			"-nostats",
			"-loglevel", "quiet",
			outputPath,
		}
	} else if strings.HasPrefix(mimeType, "video") {
		ffmpegArgs = []string{
			"-i", inputPath,
			"-c:v", "h264_nvenc",
			"-preset", "p3",
			"-rc", "vbr_hq",
			"-cq", "30",
			"-b:v", "0",
			"-maxrate", "3000k",
			"-bufsize", "6000k",
			"-profile:v", "high",
			"-level:v", "4.1",
			"-vf", "scale=w=854:h=480:force_original_aspect_ratio=decrease",
			"-c:a", "aac",
			"-b:a", "64k",
			"-f", "mp4",
			"-hide_banner",
			"-nostats",
			"-loglevel", "debug",
			outputPath,
		}
	} else if strings.HasPrefix(mimeType, "audio") {
		ffmpegArgs = []string{
			"-i", inputPath,
			"-vn",
			"-ar", "44100",
			"-ac", "2",
			"-b:a", "96k",
			"-threads", "8",
			"-hide_banner",
			"-nostats",
			"-loglevel", "quiet",
			outputPath,
		}
	} else {
		// Default video settings for unknown types
		ffmpegArgs = []string{
			"-i", inputPath,
			"-c:v", "h264_nvenc",
			"-preset", "p3",
			"-rc", "vbr_hq",
			"-cq", "30",
			"-b:v", "0",
			"-maxrate", "3000k",
			"-bufsize", "6000k",
			"-profile:v", "high",
			"-level:v", "4.1",
			"-vf", "scale=w=854:h=480:force_original_aspect_ratio=decrease",
			"-c:a", "aac",
			"-b:a", "64k",
			"-f", "mp4",
			"-hide_banner",
			"-nostats",
			"-loglevel", "debug",
			outputPath,
		}
	}

	ffmpegArgs = append(ffmpegArgs, "-progress", "pipe:1")

	cmd := exec.Command(ffmpegPath, ffmpegArgs...)
	f.Controller.currentProcess = cmd

	var stderrBuf bytes.Buffer
	cmd.Stderr = &stderrBuf

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return "", fmt.Errorf("StdoutPipe oluşturulamadı: %v", err)
	}

	if err := cmd.Start(); err != nil {
		return "", fmt.Errorf("FFmpeg başlatılamadı: %v - %s", err, stderrBuf.String())
	}

	duration, err := f.getDurationAdvanced(inputPath, ffprobePath)
	if err != nil {
		f.Controller.EmitEvent(EventWarning, "Duration could not be determined", 0, f.Id, "COMPRESSION", nil, err)
	}

	// Progress tracking goroutine
	go func() {
		defer func() {
			if r := recover(); r != nil {
				f.Controller.EmitEvent(EventError, fmt.Sprintf("Progress tracking panic: %v", r), 0, f.Id, "COMPRESSION", nil, fmt.Errorf("%v", r))
			}
		}()

		scanner := bufio.NewScanner(stdoutPipe)
		reTime := regexp.MustCompile(`time=(\d+:\d+:\d+\.\d+)`)
		reProgressEnd := regexp.MustCompile(`progress=end`)

		for scanner.Scan() {
			// Context ve pause kontrolü
			if err := f.Controller.CheckContinue(); err != nil {
				return
			}

			line := scanner.Text()

			if reProgressEnd.MatchString(line) {
				f.Controller.EmitEvent(EventProgress, "Compression completed", 100, f.Id, "COMPRESSION", nil, nil)
				break
			} else {
				matchTime := reTime.FindStringSubmatch(line)
				if len(matchTime) > 1 && duration > 0 {
					currentTime, err := parseTimeAdvanced(matchTime[1])
					if err == nil {
						percentage := int(float64(currentTime) / float64(duration) * 100)
						if percentage > 100 {
							percentage = 100
						}
						f.Controller.EmitEvent(EventProgress, fmt.Sprintf("Compression progress: %d%%", percentage), float64(percentage), f.Id, "COMPRESSION", map[string]interface{}{
							"current_time": currentTime,
							"total_time":   duration,
						}, nil)
					}
				}
			}
		}

		if err := scanner.Err(); err != nil {
			f.Controller.EmitEvent(EventError, "Error reading FFmpeg stdout", 0, f.Id, "COMPRESSION", nil, err)
		}
	}()

	// Stderr tracking goroutine
	go func() {
		defer func() {
			if r := recover(); r != nil {
				log.Printf("Stderr tracking panic: %v", r)
			}
		}()

		scanner := bufio.NewScanner(&stderrBuf)
		for scanner.Scan() {
			line := scanner.Text()
			if strings.Contains(strings.ToLower(line), "error") {
				f.Controller.EmitEvent(EventWarning, "FFmpeg warning: "+line, 0, f.Id, "COMPRESSION", nil, nil)
			}
		}
	}()

	if err := cmd.Wait(); err != nil {
		f.Controller.EmitEvent(EventWarning, "FFmpeg process finished with error", 0, f.Id, "COMPRESSION", nil, err)
		// Hata olsa bile devam et
		return mimeType, err
	}

	return mimeType, nil
}

// Gelişmiş thumbnail üretimi
func (f *FikoYTDosyaAdvanced) generateThumbnailAdvanced() error {
	if !f.isVideo() {
		return fmt.Errorf("not a video file")
	}

	tempDir := "./ffmpeg/files"
	f.ThumbId = primitive.NewObjectID()
	f.ThumbPath = filepath.Join(tempDir, f.ThumbId.Hex()+"_thumb.jpg")

	ffmpegArgs := []string{
		"-i", f.ProcessedPath,
		"-ss", "00:00:01",
		"-vframes", "1",
		"-vcodec", "mjpeg",
		"-q:v", "5",
		"-vf", "scale='min(1920,iw)':'min(1080,ih)':force_original_aspect_ratio=decrease",
		"-an",
		"-f", "image2",
		"-hide_banner",
		"-nostats",
		"-loglevel", "quiet",
		"-y",
		f.ThumbPath,
	}

	cmd := exec.Command("ffmpeg", ffmpegArgs...)

	var stderrBuf bytes.Buffer
	cmd.Stderr = &stderrBuf

	err := cmd.Run()
	if err != nil {
		f.Controller.EmitEvent(EventWarning, "Thumbnail generation failed: "+stderrBuf.String(), 0, f.Id, "THUMBNAIL", nil, err)
		f.ThumbId = primitive.NilObjectID
		f.ThumbPath = ""
		return err
	}

	if _, err := os.Stat(f.ThumbPath); os.IsNotExist(err) {
		f.ThumbId = primitive.NilObjectID
		f.ThumbPath = ""
		return fmt.Errorf("thumbnail file was not created")
	}

	f.ThumbGenerated = true
	return nil
}

// Gelişmiş MIME type detection
func (f *FikoYTDosyaAdvanced) detectMimeTypeAdvanced() (string, error) {
	// Önceki detectMimeType fonksiyonunuzun aynısı ama event'lerle
	defer func() {
		if r := recover(); r != nil {
			f.Controller.EmitEvent(EventWarning, fmt.Sprintf("MIME detection panic: %v", r), 0, f.Id, "MIME_DETECTION", nil, fmt.Errorf("%v", r))
		}
	}()

	return detectMimeType(f.Path) // Mevcut fonksiyonunuzu kullan
}

// Gelişmiş duration alma
func (f *FikoYTDosyaAdvanced) getDurationAdvanced(path, ffprobePath string) (float64, error) {
	cmd := exec.Command(ffprobePath, "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", path)
	output, err := cmd.Output()
	if err != nil {
		return 0, err
	}
	durationStr := strings.TrimSpace(string(output))
	duration, err := strconv.ParseFloat(durationStr, 64)
	if err != nil {
		return 0, err
	}
	return duration, nil
}

// Gelişmiş time parsing
func parseTimeAdvanced(timeStr string) (float64, error) {
	parts := strings.Split(timeStr, ":")
	if len(parts) != 3 {
		return 0, fmt.Errorf("geçersiz zaman formatı")
	}
	hours, err := strconv.Atoi(parts[0])
	if err != nil {
		return 0, err
	}
	minutes, err := strconv.Atoi(parts[1])
	if err != nil {
		return 0, err
	}
	secondsStr := strings.Replace(parts[2], ",", ".", 1)
	seconds, err := strconv.ParseFloat(secondsStr, 64)
	if err != nil {
		return 0, err
	}
	return float64(hours*3600+minutes*60) + seconds, nil
}

// Default event handler örneği
func DefaultEventHandler(event *ProcessEvent) error {
	logLevel := "INFO"
	if event.Type == EventError {
		logLevel = "ERROR"
	} else if event.Type == EventWarning {
		logLevel = "WARN"
	}

	log.Printf("[%s] [%s] [%s] %s (Progress: %.1f%%)",
		logLevel, event.FileID.Hex(), event.ProcessStep, event.Message, event.Progress)

	if event.Error != nil {
		log.Printf("[ERROR] %v", event.Error)
	}

	return nil
}

// Önceki fonksiyonlarınız (değişiklik yok)
func getDuration(path, ffprobePath string) (float64, error) {
	cmd := exec.Command(ffprobePath, "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", path)
	output, err := cmd.Output()
	if err != nil {
		return 0, err
	}
	durationStr := strings.TrimSpace(string(output))
	duration, err := strconv.ParseFloat(durationStr, 64)
	if err != nil {
		return 0, err
	}
	return duration, nil
}

func parseTime(timeStr string) (float64, error) {
	parts := strings.Split(timeStr, ":")
	if len(parts) != 3 {
		return 0, fmt.Errorf("geçersiz zaman formatı")
	}
	hours, err := strconv.Atoi(parts[0])
	if err != nil {
		return 0, err
	}
	minutes, err := strconv.Atoi(parts[1])
	if err != nil {
		return 0, err
	}
	secondsStr := strings.Replace(parts[2], ",", ".", 1)
	seconds, err := strconv.ParseFloat(secondsStr, 64)
	if err != nil {
		return 0, err
	}
	return float64(hours*3600+minutes*60) + seconds, nil
}

// GridFS fonksiyonları (değişiklik yok)
func saveToGridFS(fileID primitive.ObjectID, filePath string, metaData *fiber.Map) (primitive.ObjectID, error) {
	db := fiko_mongo.GetMongoDBClient().Database("fikoyt")
	fsName := "go_files"
	bucket, err := gridfs.NewBucket(db, &options.BucketOptions{Name: &fsName})
	if err != nil {
		return primitive.NilObjectID, fiber.NewError(fiber.StatusInternalServerError, "GridFS Bucket oluşturulamadı: "+err.Error())
	}

	file, err := os.Open(filePath)
	if err != nil {
		return primitive.NilObjectID, fmt.Errorf("dosya açılamadı: %v", err)
	}
	defer file.Close()

	uploadStream, err := bucket.OpenUploadStreamWithID(fileID, strings.ReplaceAll(filepath.Base(filePath), "-processed", ""), &options.UploadOptions{Metadata: metaData})
	if err != nil {
		return primitive.NilObjectID, fmt.Errorf("upload stream oluşturulamadı: %v", err)
	}
	defer uploadStream.Close()

	if _, err := io.Copy(uploadStream, file); err != nil {
		return primitive.NilObjectID, fmt.Errorf("dosya GridFS'ye yazılamadı: %v", err)
	}

	return fileID, nil
}

func DeleteFromGridfs(fileID primitive.ObjectID) error {
	db := fiko_mongo.GetMongoDBClient().Database("fikoyt")
	fsName := "go_files"
	bucket, err := gridfs.NewBucket(db, &options.BucketOptions{Name: &fsName})
	if err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "GridFS Bucket oluşturulamadı: "+err.Error())
	}

	return bucket.Delete(fileID)
}

// Gelişmiş GridFS kaydetme işlemi
func (f *FikoYTDosyaAdvanced) KaydetGridFsAdvanced(metadata fiber.Map) error {
	f.Controller.EmitEvent(EventGridFSSave, "Starting GridFS save", 0, f.Id, "GRIDFS_SAVE", nil, nil)

	defer func() {
		if r := recover(); r != nil {
			f.Controller.EmitEvent(EventError, fmt.Sprintf("Panic during GridFS save: %v", r), 0, f.Id, "GRIDFS_SAVE", nil, fmt.Errorf("%v", r))
		}
	}()

	fileOid := f.Id
	_, err := saveToGridFS(fileOid, f.ProcessedPath, &metadata)
	if err != nil {
		f.Controller.EmitEvent(EventError, "Error saving to GridFS", 100, f.Id, "GRIDFS_SAVE", nil, err)
		// Hata olsa bile devam etmek için nil döndürün
		return nil // veya return err (durmasını istiyorsanız)
	}

	f.Controller.EmitEvent(EventCompleted, "GridFS save completed", 100, f.Id, "GRIDFS_SAVE", map[string]interface{}{
		"file_id": fileOid.Hex(),
	}, nil)

	return nil
}

// Gelişmiş thumbnail GridFS kaydetme
func (f *FikoYTDosyaAdvanced) KaydetThumbGridFsAdvanced(metadata fiber.Map) error {
	if !f.ThumbGenerated || f.ThumbPath == "" {
		f.Controller.EmitEvent(EventWarning, "No thumbnail to save", 0, f.Id, "THUMB_GRIDFS_SAVE", nil, nil)
		return nil
	}

	f.Controller.EmitEvent(EventGridFSSave, "Starting thumbnail GridFS save", 0, f.ThumbId, "THUMB_GRIDFS_SAVE", nil, nil)

	defer func() {
		if r := recover(); r != nil {
			f.Controller.EmitEvent(EventError, fmt.Sprintf("Panic during thumbnail GridFS save: %v", r), 0, f.ThumbId, "THUMB_GRIDFS_SAVE", nil, fmt.Errorf("%v", r))
		}
	}()

	if metadata == nil {
		metadata = fiber.Map{}
	}
	metadata["type"] = "thumbnail"
	metadata["parent_file_id"] = f.Id.Hex()
	metadata["mime_type"] = "image/jpeg"

	_, err := saveToGridFS(f.ThumbId, f.ThumbPath, &metadata)
	if err != nil {
		f.Controller.EmitEvent(EventWarning, "Error saving thumbnail to GridFS", 100, f.ThumbId, "THUMB_GRIDFS_SAVE", nil, err)
		// Thumbnail kaydedilemedi, ID'yi sıfırla ama hata döndürme
		f.ThumbId = primitive.NilObjectID
		f.ThumbGenerated = false
		return nil
	}

	f.Controller.EmitEvent(EventCompleted, "Thumbnail GridFS save completed", 100, f.ThumbId, "THUMB_GRIDFS_SAVE", map[string]interface{}{
		"thumb_id": f.ThumbId.Hex(),
	}, nil)

	return nil
}

// Gelişmiş temizleme işlemi
func (f *FikoYTDosyaAdvanced) TemizleDosyalarAdvanced() error {
	f.Controller.EmitEvent(EventCleanup, "Starting file cleanup", 0, f.Id, "CLEANUP", nil, nil)

	defer func() {
		if r := recover(); r != nil {
			f.Controller.EmitEvent(EventError, fmt.Sprintf("Panic during cleanup: %v", r), 0, f.Id, "CLEANUP", nil, fmt.Errorf("%v", r))
		}
	}()

	cleanupCount := 0
	totalFiles := 0

	// Ana dosyayı sil
	if f.Path != "" {
		totalFiles++
		if _, err := os.Stat(f.Path); err == nil {
			if removeErr := os.Remove(f.Path); removeErr == nil {
				cleanupCount++
				f.Controller.EmitEvent(EventProgress, "Original file cleaned", 33, f.Id, "CLEANUP", map[string]interface{}{
					"file": f.Path,
				}, nil)
			} else {
				f.Controller.EmitEvent(EventWarning, "Failed to remove original file", 33, f.Id, "CLEANUP", nil, removeErr)
			}
		}
	}

	// İşlenmiş dosyayı sil
	if f.ProcessedPath != "" && f.ProcessedPath != f.Path {
		totalFiles++
		if _, err := os.Stat(f.ProcessedPath); err == nil {
			if removeErr := os.Remove(f.ProcessedPath); removeErr == nil {
				cleanupCount++
				f.Controller.EmitEvent(EventProgress, "Processed file cleaned", 66, f.Id, "CLEANUP", map[string]interface{}{
					"file": f.ProcessedPath,
				}, nil)
			} else {
				f.Controller.EmitEvent(EventWarning, "Failed to remove processed file", 66, f.Id, "CLEANUP", nil, removeErr)
			}
		}
	}

	// Thumbnail dosyasını sil
	if f.ThumbPath != "" {
		totalFiles++
		if _, err := os.Stat(f.ThumbPath); err == nil {
			if removeErr := os.Remove(f.ThumbPath); removeErr == nil {
				cleanupCount++
				f.Controller.EmitEvent(EventProgress, "Thumbnail file cleaned", 100, f.Id, "CLEANUP", map[string]interface{}{
					"file": f.ThumbPath,
				}, nil)
			} else {
				f.Controller.EmitEvent(EventWarning, "Failed to remove thumbnail file", 100, f.Id, "CLEANUP", nil, removeErr)
			}
		}
	}

	f.Controller.EmitEvent(EventCompleted, fmt.Sprintf("Cleanup completed: %d/%d files cleaned", cleanupCount, totalFiles), 100, f.Id, "CLEANUP", map[string]interface{}{
		"cleaned_files": cleanupCount,
		"total_files":   totalFiles,
	}, nil)

	return nil
}

// Tüm işlemleri tek seferde çalıştırma
func (f *FikoYTDosyaAdvanced) TumIslemleriCalistir(metadata fiber.Map) error {
	f.Controller.EmitEvent(EventStarted, "Starting full file processing pipeline", 0, f.Id, "FULL_PIPELINE", nil, nil)

	// 1. Dosyayı kaydet
	if err := f.KaydetAdvanced(); err != nil {
		f.Controller.EmitEvent(EventWarning, "Save failed, continuing...", 20, f.Id, "FULL_PIPELINE", nil, err)
	}

	// 2. İşle (sıkıştır ve thumbnail üret)
	if err := f.IsleAdvanced(); err != nil {
		f.Controller.EmitEvent(EventWarning, "Processing failed, continuing...", 50, f.Id, "FULL_PIPELINE", nil, err)
	}

	// 3. GridFS'ye kaydet
	if err := f.KaydetGridFsAdvanced(metadata); err != nil {
		f.Controller.EmitEvent(EventWarning, "GridFS save failed, continuing...", 70, f.Id, "FULL_PIPELINE", nil, err)
	}

	// 4. Thumbnail'ı GridFS'ye kaydet
	if err := f.KaydetThumbGridFsAdvanced(metadata); err != nil {
		f.Controller.EmitEvent(EventWarning, "Thumbnail GridFS save failed, continuing...", 90, f.Id, "FULL_PIPELINE", nil, err)
	}

	// 5. Temizlik yap
	if err := f.TemizleDosyalarAdvanced(); err != nil {
		f.Controller.EmitEvent(EventWarning, "Cleanup failed, continuing...", 95, f.Id, "FULL_PIPELINE", nil, err)
	}

	f.Controller.EmitEvent(EventCompleted, "Full processing pipeline completed", 100, f.Id, "FULL_PIPELINE", map[string]interface{}{
		"main_file_id": f.Id.Hex(),
		"thumb_id":     f.ThumbId.Hex(),
		"has_thumb":    f.HasThumbnail(),
	}, nil)

	return nil
}

// Batch işleme için
type BatchProcessor struct {
	Controller *ProcessController
	Files      []*FikoYTDosyaAdvanced
	BatchID    string
}

func NewBatchProcessor(files []*FikoYTDosyaAdvanced) *BatchProcessor {
	return &BatchProcessor{
		Controller: NewProcessController(),
		Files:      files,
		BatchID:    fmt.Sprintf("batch_%d", time.Now().UnixNano()),
	}
}

func (bp *BatchProcessor) ProcessAll(metadata fiber.Map, maxConcurrent int) error {
	bp.Controller.EmitEvent(EventStarted, fmt.Sprintf("Starting batch processing: %d files", len(bp.Files)), 0, primitive.NilObjectID, "BATCH", map[string]interface{}{
		"total_files": len(bp.Files),
		"batch_id":    bp.BatchID,
	}, nil)

	if maxConcurrent <= 0 {
		maxConcurrent = 3 // Default concurrent limit
	}

	semaphore := make(chan struct{}, maxConcurrent)
	var wg sync.WaitGroup

	processedCount := 0
	var mu sync.Mutex

	for i, file := range bp.Files {
		wg.Add(1)
		go func(index int, f *FikoYTDosyaAdvanced) {
			defer wg.Done()

			semaphore <- struct{}{}        // Acquire
			defer func() { <-semaphore }() // Release

			// Her dosya için kendi event handler'ını ekle
			f.Controller.AddEventHandler(func(event *ProcessEvent) error {
				// Batch progress hesapla
				mu.Lock()
				if event.Type == EventCompleted && event.ProcessStep == "FULL_PIPELINE" {
					processedCount++
				}
				currentProgress := float64(processedCount) / float64(len(bp.Files)) * 100
				mu.Unlock()

				// Batch event emit et
				bp.Controller.EmitEvent(EventProgress,
					fmt.Sprintf("File %d/%d - %s", index+1, len(bp.Files), event.Message),
					currentProgress,
					event.FileID,
					"BATCH",
					map[string]interface{}{
						"file_index":    index + 1,
						"total_files":   len(bp.Files),
						"current_file":  event.FileID.Hex(),
						"current_step":  event.ProcessStep,
						"file_progress": event.Progress,
					},
					event.Error)
				return nil
			})

			// Dosyayı işle
			if err := f.TumIslemleriCalistir(metadata); err != nil {
				bp.Controller.EmitEvent(EventWarning,
					fmt.Sprintf("File %d processing failed: %v", index+1, err),
					0, f.Id, "BATCH", nil, err)
			}
		}(i, file)
	}

	wg.Wait()

	bp.Controller.EmitEvent(EventCompleted,
		fmt.Sprintf("Batch processing completed: %d/%d files processed", processedCount, len(bp.Files)),
		100, primitive.NilObjectID, "BATCH", map[string]interface{}{
			"processed_files": processedCount,
			"total_files":     len(bp.Files),
			"batch_id":        bp.BatchID,
		}, nil)

	return nil
}

// Kullanım örnekleri ve yardımcı fonksiyonlar

// Real-time event streaming için WebSocket handler
// func (pc *ProcessController) StreamEventsWebSocket(c *websocket.Conn) {
// 	// Event handler ekle
// 	eventChan := make(chan *ProcessEvent, 100)

// 	handler := func(event *ProcessEvent) error {
// 		select {
// 		case eventChan <- event:
// 		default:
// 			// Buffer full, skip event
// 		}
// 		return nil
// 	}

// 	pc.AddEventHandler(handler)

// 	// WebSocket'e event'leri gönder
// 	go func() {
// 		defer close(eventChan)
// 		for event := range eventChan {
// 			if err := c.WriteJSON(event); err != nil {
// 				break
// 			}
// 		}
// 	}()
// }

// HTTP endpoint için event listesi
func (pc *ProcessController) GetEventsJSON() map[string]interface{} {
	events := pc.GetEvents()
	return map[string]interface{}{
		"status":       pc.GetStatus(),
		"total_events": len(events),
		"events":       events,
		"last_event": func() *ProcessEvent {
			if len(events) > 0 {
				return events[len(events)-1]
			}
			return nil
		}(),
	}
}

// Process durumu için özet bilgi
func (pc *ProcessController) GetStatusSummary() map[string]interface{} {
	pc.mu.RLock()
	defer pc.mu.RUnlock()

	events := pc.events
	var lastProgress float64
	var lastError *ProcessEvent
	var warningCount int
	var errorCount int

	for _, event := range events {
		if event.Progress > lastProgress {
			lastProgress = event.Progress
		}
		if event.Type == EventError {
			errorCount++
			lastError = event
		}
		if event.Type == EventWarning {
			warningCount++
		}
	}

	return map[string]interface{}{
		"status":        pc.status,
		"progress":      lastProgress,
		"total_events":  len(events),
		"error_count":   errorCount,
		"warning_count": warningCount,
		"last_error":    lastError,
		"is_running":    pc.status == StatusRunning,
		"is_paused":     pc.status == StatusPaused,
		"is_completed":  pc.status == StatusCompleted,
		"can_pause":     pc.status == StatusRunning,
		"can_resume":    pc.status == StatusPaused,
		"can_cancel":    pc.status == StatusRunning || pc.status == StatusPaused,
	}
}
