package FikoYTDosya

import (
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/gofiber/fiber/v3"
	"go.mongodb.org/mongo-driver/bson/primitive"
)

// FikoYTDosya: Dosya ile ilgili bilgileri tutan struct
type FikoYTDosya struct {
	File          *multipart.FileHeader
	Id            primitive.ObjectID
	ThumbId       primitive.ObjectID
	Path          string
	ProcessedPath string
	MimeType      string
	Saved         bool
	Processed     bool
}

// 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
}

// Save: Dosyayı kaydetme işlemi
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 = f.Path + filepath.Ext(f.File.Filename)
	return nil
}

// Save: Dosyayı kaydetme işlemi
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
}

// Save: Dosyayı kaydetme işlemi
func (f *FikoYTDosya) SilGridFs() error {

	// İşlenen dosyayı GridFS'ye kaydet
	fileOid := f.Id
	err := DeleteFromGridfs(fileOid)
	if err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "Error saving to GridFS: "+err.Error())
	}

	return nil
}

func (f *FikoYTDosya) TemizleDosyalar() error {
	_, err := os.Stat(f.Path)
	if os.IsNotExist(err) {

	}

	// Dosya varsa, sil
	err = os.Remove(f.Path)
	if err != nil {
	}

	_, err1 := os.Stat(f.ProcessedPath)
	if os.IsNotExist(err1) {

	}

	// Dosya varsa, sil
	err1 = os.Remove(f.ProcessedPath)
	if err1 != nil {
	}
	return nil
}

// ffprobe ile MIME tipi tespit etpackage fiko

// detectMimeType dosyanın MIME türünü belirler.
// Öncelikli olarak dosya uzantısına bakar, sonra ffprobe ile kontrol eder.
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 ".mp3":
		return "audio/mpeg", nil
	case ".wav":
		return "audio/wav", nil
	}

	// 2️⃣ ffprobe ile stream tiplerini kontrol et (resim dosyaları bu noktaya gelmez)
	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
}
