package kokDB

import (
	"context"
	"encoding/base64"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"path/filepath"
	"strings"
	mail_dinleyici "server/apps/mail_dinleyici"
	"server/sistem/FikoYTDosya"
	FikoYTCtx "server/sistem/fikoyt_ctx"
	FikoYTMap "server/sistem/fikoyt_map"
	"time"

	"github.com/gofiber/fiber/v3"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

// objectIdToHex converts various _id types to hex string
func objectIdToHex(id interface{}) string {
	switch v := id.(type) {
	case primitive.ObjectID:
		return v.Hex()
	case string:
		return v
	}
	return ""
}

// mailMesajSilHook - mail silindiğinde GridFS eklerini de siler
// vtMap: vt kontrolü için ham filtre; silFiltre: gerçekten silinecek kayıtların (kısıtlanmış) filtresi
func mailMesajSilHook(koleksiyon *mongo.Collection, vtMap FikoYTMap.FikoYTMap, silFiltre interface{}) {
	vt, _ := vtMap["vt"].(string)
	if vt != "FikoYT.sistem.mail.mesajlar" {
		return
	}
	// Silinecek mailin eklerini bul
	ctx := context.TODO()
	cursor, _ := koleksiyon.Find(ctx, silFiltre, options.Find().SetProjection(bson.M{"ekler.dosyaId": 1}))
	if cursor == nil {
		return
	}
	defer cursor.Close(ctx)

	db := koleksiyon.Database()
	bucket, err := mail_dinleyici.GridFSBucket(db)
	if err != nil {
		return
	}

	for cursor.Next(ctx) {
		var doc bson.M
		if cursor.Decode(&doc) != nil {
			continue
		}
		ekler, _ := doc["ekler"].(primitive.A)
		for _, ek := range ekler {
			if ekMap, ok := ek.(bson.M); ok {
				if fid, ok := ekMap["dosyaId"].(primitive.ObjectID); ok {
					bucket.Delete(fid)
				}
			}
		}
	}
}

// mailHesapEkleHook - mail hesabı eklendiğinde dinleyiciyi başlatır
// ekle sonucu InsertedID döner, onu kullanarak hesabı başlat
func mailHesapEkleHook(json FikoYTMap.FikoYTMap, sonuc interface{}) {
	vt, _ := json["vt"].(string)
	if vt != "FikoYT.sistem.mail.hesaplar" {
		return
	}
	// sonuc InsertOneResult veya map olabilir - InsertedID'yi al
	var hesapID string
	switch r := sonuc.(type) {
	case *mongo.InsertOneResult:
		hesapID = objectIdToHex(r.InsertedID)
	case map[string]interface{}:
		hesapID = objectIdToHex(r["_id"])
	}
	// Fallback: json'dan _id
	if hesapID == "" {
		hesapID = objectIdToHex(json["_id"])
	}
	if hesapID != "" {
		go mail_dinleyici.YenidenBaslatHesap(hesapID)
		log.Printf("[kokDB] Mail hesap eklendi, dinleyici baslatiliyor: %s", hesapID)
	}
}

// mailHesapGuncelleHook - mail hesabı güncellendiğinde dinleyiciyi yeniden başlatır
// Filter'da vt olmayabilir (sadece _id), bu yüzden DB'den kontrol et
func mailHesapGuncelleHook(koleksiyon *mongo.Collection, filterMap FikoYTMap.FikoYTMap) {
	hesapID := objectIdToHex(filterMap["_id"])
	if hesapID == "" {
		return
	}

	// Filter'da vt varsa direkt kontrol et
	if vt, ok := filterMap["vt"].(string); ok {
		if vt == "FikoYT.sistem.mail.hesaplar" {
			go mail_dinleyici.YenidenBaslatHesap(hesapID)
			log.Printf("[kokDB] Mail hesap guncellendi: %s", hesapID)
		}
		return
	}

	// vt yoksa DB'den kontrol et - bu hesap mail hesabı mı?
	oid, err := primitive.ObjectIDFromHex(hesapID)
	if err != nil {
		return
	}
	var doc bson.M
	koleksiyon.FindOne(context.TODO(), bson.M{"_id": oid}, options.FindOne().SetProjection(bson.M{"vt": 1})).Decode(&doc)
	if doc != nil {
		if vt, ok := doc["vt"].(string); ok && vt == "FikoYT.sistem.mail.hesaplar" {
			go mail_dinleyici.YenidenBaslatHesap(hesapID)
			log.Printf("[kokDB] Mail hesap guncellendi: %s", hesapID)
		}
	}
}

func convertStagesToPipeline(stages []interface{}) (mongo.Pipeline, error) {
	var pipeline mongo.Pipeline
	for _, stage := range stages {
		var doc map[string]interface{}

		switch s := stage.(type) {
		case map[string]interface{}:
			doc = s
		case FikoYTMap.FikoYTMap:
			doc = map[string]interface{}(s) // Tip dönüşümü
		default:
			return nil, fmt.Errorf("stage tipi map[string]interface{} değil, %T", stage)
		}

		bsonDoc := bson.D{}
		for k, v := range doc {
			bsonDoc = append(bsonDoc, bson.E{Key: k, Value: v})
		}
		pipeline = append(pipeline, bsonDoc)
	}
	return pipeline, nil
}

func fikoyt_sistem_db_agregate(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	db := c.Params("db")

	// Güvenlik: beyaz liste + rol denetimi (bkz. guvenlik.go). :db istemciden
	// geldiği için bu kontrol OLMADAN aggregate tüm DB'yi okur/yazabilir.
	if err := dbErisimKontrol(FikoYT, db); err != nil {
		return err
	}

	koleksiyon := FikoYT.Koleksiyon(db)
	// var dbMap FikoYTMap.FikoYTMap
	ctx := context.TODO()

	var results []bson.M

	FikoYT.Istek.Json.IsleObjectId()

	aggrRaw, ok := FikoYT.Istek.Json["aggregate"]
	if !ok {
		return c.Status(200).JSON(FikoYTMap.FikoYTMap{})
	}

	aggrSlice, ok := aggrRaw.([]interface{})
	if !ok {
		return c.Status(200).JSON(FikoYTMap.FikoYTMap{})
	}

	// ⚠️ GÜVENLİK: aggregate pipeline "$out" ve "$merge" stage'leri içerirse
	//    aggregate aslında KALICI YAZMA yapar — $out sonuçları hedef koleksiyona
	//    boşaltır, $merge mevcut belgeleri günceller/ekler. Bu, beyaz listedeki
	//    bir koleksiyona okuma yetkisi olan HERKES için keyfi yazma açığıdır.
	//    Bu yüzden yazma aşamaları yalnız yönetici/üreticiye açıktır.
	if ureticiVeyaYoneticiMi(FikoYT) == false {
		for _, ham := range aggrSlice {
			asama, asamaOk := ham.(map[string]interface{})
			if !asamaOk {
				continue
			}
			for anahtar := range asama {
				if anahtar == "$out" || anahtar == "$merge" {
					return fiber.NewError(fiber.StatusForbidden,
						"Aggregate içinde "+anahtar+" kullanılamaz (yazma yetkisi gerekli)")
				}
			}
		}
	}

	pipeline, err := convertStagesToPipeline(aggrSlice)
	if err != nil {
		fmt.Println("Pipeline dönüşüm hatası:", err)
		return c.Status(200).JSON(FikoYTMap.FikoYTMap{})
	}

	cursor, err := koleksiyon.Aggregate(ctx, pipeline, &options.AggregateOptions{})
	// Varsayılanlar
	if err != nil {

		return nil
	}

	defer cursor.Close(ctx)

	cursor.All(ctx, &results)

	// prettyJson, _ := json.MarshalIndent(pipeline, "", "  ")
	// fmt.Println("Pipeline JSON:", string(prettyJson))

	// DB'den gelen sonuç (örnek)
	FikoYT.Cevap.Sonuc = results

	return c.JSON(results)
}

func fikoyt_sistem_db_ekle(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	db := c.Params("db")

	// Güvenlik: beyaz liste + rol denetimi (bkz. guvenlik.go)
	if err := dbErisimKontrolYazma(true, FikoYT, db, true); err != nil {
		return err
	}

	koleksiyon := FikoYT.Koleksiyon(db)

	// var dbMap FikoYTMap.FikoYTMap
	// ctx := context.TODO()

	FikoYT.Istek.Json.IsleObjectId()
	// koleksiyon.CountDocuments(context.TODO(), FikoYTMap.FikoYTMap{})
	//------------------------------------->

	// Güvenlik: korunan koleksiyonlarda oturum + sahiplik denetimi (bkz. guvenlik.go)
	if korunanKoleksiyonMu(db) {
		switch FikoYT.Istek.JsonTip {
		case "Object":
			if izinli, kod, mesaj := hareketYazmaKontrol(FikoYT, koleksiyon, FikoYT.Istek.Json); !izinli {
				return c.Status(kod).JSON(fiber.Map{"hata": mesaj})
			}
		case "Array":
			for _, oge := range FikoYT.Istek.JsonArray {
				var belge FikoYTMap.FikoYTMap
				switch v := oge.(type) {
				case FikoYTMap.FikoYTMap:
					belge = v
				case map[string]interface{}:
					belge = FikoYTMap.FikoYTMap(v)
				}
				if izinli, kod, mesaj := hareketYazmaKontrol(FikoYT, koleksiyon, belge); !izinli {
					return c.Status(kod).JSON(fiber.Map{"hata": mesaj})
				}
			}
		}
		// Dosya boyutu sınırı
		for _, dosya := range FikoYT.Istek.Dosyalar {
			if dosya.File != nil && dosya.File.Size > MaxDosyaBoyutu {
				return c.Status(413).JSON(fiber.Map{"hata": "Dosya çok büyük (en fazla 500 MB)"})
			}
		}
	}

	var sonuclar interface{} = nil
	var err interface{} = nil

	switch FikoYT.Istek.JsonTip {
	case "Object":
		sonuclar, err = FikoYT.EkleYadaGuncelle(koleksiyon, FikoYT.Istek.Json)
		if err != nil {
			fmt.Println(err)
			sonuclar = err
		}
	case "Array":
		sonuclar, err = FikoYT.EkleYadaGuncelle(koleksiyon, FikoYT.Istek.JsonArray)
		if err != nil {
			fmt.Println(err)
			sonuclar = err
		}
	default:

	}
	// rslt, err := koleksiyon.InsertOne(ctx, FikoYT.Istek.Json)

	//------------------------------------->
	if len(FikoYT.Istek.Dosyalar) > 0 {
		// dosya := FikoYT.Istek.Dosyalar[0]
		// dosya.Kaydet()
		// dosya.Isle()
		// dosya.KaydetGridFs(fiber.Map{"yol": "./hareketler/" + db, "kid": FikoYT.AktifKullanici.Id})
		// Video posterleri (istemciden "poster_<id>" alanıyla gelir)
		posterler, _ := c.Locals("posterler").(map[string]*multipart.FileHeader)
		for _, dosya := range FikoYT.Istek.Dosyalar {
			dosya.Kaydet()
			// Video ve poster seçilmişse: poster, işlemeden önce thumbnail olarak hazırlanır
			if posterHdr, ok := posterler[dosya.Id.Hex()]; ok && (strings.HasPrefix(dosya.MimeType, "video/") || strings.HasPrefix(dosya.MimeType, "audio/")) {
				// kaydetInternal, Path'in dolu olmasını ister (dosya adı) ve Path'i geçici yola dönüştürür
				p := FikoYTDosya.FikoYTDosya{File: posterHdr, Id: primitive.NewObjectID(), Path: posterHdr.Filename}
				if err := p.Kaydet(); err != nil {
					log.Printf("[poster] kaydı başarısız (dosya %s): %v", dosya.Id.Hex(), err)
				} else {
					dosya.ThumbId = primitive.NewObjectID()
					dosya.ThumbPath = p.Path
					dosya.ThumbGenerated = true
				}
			}
			// İlerleme yayını: kartı görebilen istemcilere gider; kart id'si _uid (üst kart) veya _id
			yayinci := &dosyaIslemYayinci{kartId: objectIdToHex(FikoYT.Istek.Json["_uid"])}
			if yayinci.kartId == "" {
				yayinci.kartId = objectIdToHex(FikoYT.Istek.Json["_id"])
			}
			go func(d FikoYTDosya.FikoYTDosya) {
				d.EnableEvents().
					AddEventHandler(func(event *FikoYTDosya.ProcessEvent) error {
						yayinci.Yayinla(event)
						// logLevel := "INFO"
						// switch event.Type {
						// case FikoYTDosya.EventError:
						// 	logLevel = "ERROR"
						// case FikoYTDosya.EventWarning:
						// 	logLevel = "WARN"
						// }

						if event.Type == FikoYTDosya.EventProgress && event.ProcessStep == "COMPRESSION" {
							log.Printf("(İlerleme: %.3f%%)", event.Progress)
						}

						// 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
					}).IsleEventli()
				d.KaydetGridFsEventli(fiber.Map{"yol": "./hareketler/" + db, "kid": FikoYT.AktifKullanici.Id})
				d.KaydetThumbGridFsEventli(fiber.Map{"yol": "./hareketler/" + db, "kid": FikoYT.AktifKullanici.Id, "thumb": true, "file_id": d.Id})
				d.TemizleEventli()
				// TumIslemleriYapEventli(
				// 	,
				// 	,
				// )
			}(dosya)

			// dosya.EnableEvents().
			// 	AddEventHandler(FikoYTDosya.DefaultEventHandler).
			// 	TumIslemleriYapEventli(fiber.Map{"yol": "./hareketler/" + db, "kid": FikoYT.AktifKullanici.Id}, fiber.Map{"yol": "./hareketler/" + db, "kid": FikoYT.AktifKullanici.Id, "thumb": true, "file_id": dosya.Id})

			// dosya.Kaydet()
			// dosya.Isle()
			// dosya.KaydetGridFs(fiber.Map{"yol": "./hareketler/" + db, "kid": FikoYT.AktifKullanici.Id})
			// dosya.KaydetThumbGridFs(fiber.Map{"yol": "./hareketler/" + db, "kid": FikoYT.AktifKullanici.Id, "thumb": true, "file_id": dosya.Id})
			// dosya.TemizleDosyalar()
		}

	}

	// DB'den gelen sonuç (örnek)
	FikoYT.Cevap.Sonuc = sonuclar

	// Mail hesap hook
	if db == "hareketler" {
		mailHesapEkleHook(FikoYT.Istek.Json, sonuclar)
	}

	// Paylaşım kartı canlı yayın (Socket.IO)
	if db == "hareketler" && FikoYT.Istek.JsonTip == "Object" && err == nil {
		ekleSonrasiYayinla(FikoYT, FikoYT.Istek.Json, sonuclar)
	}

	return c.JSON(sonuclar)
}

func fikoyt_sistem_db_sil(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	db := c.Params("db")

	// Güvenlik: beyaz liste + rol denetimi (bkz. guvenlik.go)
	if err := dbErisimKontrolYazma(true, FikoYT, db, true); err != nil {
		return err
	}

	koleksiyon := FikoYT.Koleksiyon(db)
	ctx := context.TODO()

	// Gelen filtreleri işle
	FikoYT.Istek.Json.IsleObjectId()

	// Silme işlemi sonucu
	var sonuc *mongo.DeleteResult
	var err error

	// Güvenlik: korunan koleksiyonlarda silme filtresine sahiplik kısıtı (bkz. guvenlik.go)
	korunan := korunanKoleksiyonMu(db)

	switch FikoYT.Istek.JsonTip {
	case "Object":
		var silFiltre interface{} = FikoYT.Istek.Json
		if korunan {
			kisitli, izinli, kod, mesaj := hareketFiltreKisitla(FikoYT, FikoYT.Istek.Json)
			if !izinli {
				return c.Status(kod).JSON(fiber.Map{"hata": mesaj})
			}
			silFiltre = kisitli
		}
		// Mail mesaj silme hook - GridFS eklerini temizle (silinecek kayıtlarla aynı filtre)
		if db == "hareketler" {
			mailMesajSilHook(koleksiyon, FikoYT.Istek.Json, silFiltre)
		}
		sonuc, err = koleksiyon.DeleteMany(ctx, silFiltre)
	case "Array":
		// Array ile gelen filtrelerin her biri için silme işlemi yapılır
		var toplamSilinen int64 = 0
		for _, filtre := range FikoYT.Istek.JsonArray {
			if m, ok := filtre.(FikoYTMap.FikoYTMap); ok {
				m.IsleObjectId()
				var silFiltre interface{} = m
				if korunan {
					kisitli, izinli, kod, mesaj := hareketFiltreKisitla(FikoYT, m)
					if !izinli {
						return c.Status(kod).JSON(fiber.Map{"hata": mesaj})
					}
					silFiltre = kisitli
				}
				if db == "hareketler" {
					mailMesajSilHook(koleksiyon, m, silFiltre)
				}
				if r, e := koleksiyon.DeleteMany(ctx, silFiltre); e == nil {
					toplamSilinen += r.DeletedCount
				}
			}
		}
		// Custom response için sahte DeleteResult döndürüyoruz
		sonuc = &mongo.DeleteResult{DeletedCount: toplamSilinen}
	default:
		return c.Status(400).JSON(fiber.Map{"hata": "Geçersiz JsonTip"})
	}

	if err != nil {
		fmt.Println("Silme hatası:", err)
		return c.Status(500).JSON(false)
	}

	// Paylaşım kartı canlı yayın (Socket.IO)
	if db == "hareketler" && sonuc != nil && sonuc.DeletedCount > 0 && paylasimVtMi(FikoYT.Istek.Json) {
		if kartId := objectIdToHex(FikoYT.Istek.Json["_id"]); kartId != "" {
			paylasimKartYayinla(FikoYT, "silindi", kartId)
		}
	}

	FikoYT.Cevap.Sonuc = sonuc
	return c.JSON(true)
}

func fikoyt_sistem_db_guncelle(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	db := c.Params("db")

	// Güvenlik: beyaz liste + rol denetimi (bkz. guvenlik.go)
	if err := dbErisimKontrolYazma(true, FikoYT, db, true); err != nil {
		return err
	}

	koleksiyon := FikoYT.Koleksiyon(db)
	ctx := context.TODO()

	// Gelen JSON'u işle (ObjectId dönüştür vs.)
	FikoYT.Istek.Json.IsleObjectId()

	// 1. Filter al ve tip kontrolü
	filterRaw, ok := FikoYT.Istek.Json["filter"]
	if !ok {
		return c.Status(400).JSON(fiber.Map{"hata": "filter alanı zorunludur"})
	}

	var filterMap FikoYTMap.FikoYTMap
	switch v := filterRaw.(type) {
	case map[string]interface{}:
		filterMap = FikoYTMap.FikoYTMap(v)
	case FikoYTMap.FikoYTMap:
		filterMap = v
	default:
		return c.Status(400).JSON(fiber.Map{"hata": "filter tipi hatalı"})
	}
	filterMap.IsleObjectId()

	// 2. Update verilerini al (veri / set / data)
	updateRaw, ok := FikoYT.Istek.Json["veri"]
	if !ok {
		updateRaw, ok = FikoYT.Istek.Json["set"]
	}
	if !ok {
		updateRaw, ok = FikoYT.Istek.Json["data"]
	}
	if !ok {
		return c.Status(400).JSON(fiber.Map{"hata": "güncellenecek veri (veri / set / data) eksik"})
	}

	var updateMap FikoYTMap.FikoYTMap
	switch v := updateRaw.(type) {
	case FikoYTMap.FikoYTMap:
		updateMap = v
	case map[string]interface{}:
		updateMap = FikoYTMap.FikoYTMap(v)
	default:
		return c.Status(400).JSON(fiber.Map{"hata": "güncellenecek veri tipi hatalı"})
	}
	updateMap.IsleObjectId()

	// Güvenlik: korunan koleksiyonlarda güncelleme filtresine sahiplik kısıtı (bkz. guvenlik.go)
	var guncelleFiltre interface{} = filterMap
	kisitUygulandi := false
	if korunanKoleksiyonMu(db) {
		kisitli, izinli, kod, mesaj := hareketFiltreKisitla(FikoYT, filterMap)
		if !izinli {
			return c.Status(kod).JSON(fiber.Map{"hata": mesaj})
		}
		guncelleFiltre = kisitli
		kisitUygulandi = !yoneticiMi(FikoYT)
	}

	// 3. upsert ayarı (varsayılan true)
	upsert := true
	if u, ok := FikoYT.Istek.Json["upsert"].(bool); ok {
		upsert = u
	}
	// Sahiplik kısıtı uygulanmışsa upsert kapatılır — eşleşmeyen filtre yeni kayıt üretmesin
	if kisitUygulandi {
		upsert = false
	}
	opts := options.Update().SetUpsert(upsert)

	// 4. multi ayarı (varsayılan true)
	multi := true
	if m, ok := FikoYT.Istek.Json["multi"].(bool); ok {
		multi = m
	}

	// 5. Güncelleme işlemi
	var result *mongo.UpdateResult
	var err error

	// YAP: Eğer updateMap'te MongoDB operatörleri varsa ($set, $unset, $push vs.) direkt kullan
	// Yoksa otomatik $set içine al
	var updateDoc interface{}
	hasOperator := false
	for key := range updateMap {
		if len(key) > 0 && key[0] == '$' {
			hasOperator = true
			break
		}
	}

	if hasOperator {
		// MongoDB operatörleri var, direkt kullan
		updateDoc = updateMap
	} else {
		// Operatör yok, $set içine al
		updateDoc = bson.M{"$set": updateMap}
	}

	if multi {
		result, err = koleksiyon.UpdateMany(ctx, guncelleFiltre, updateDoc, opts)
	} else {
		result, err = koleksiyon.UpdateOne(ctx, guncelleFiltre, updateDoc, opts)
	}

	if err != nil {
		fmt.Println("Güncelleme hatası:", err)
		return c.Status(500).JSON(fiber.Map{"hata": err.Error()})
	}

	//------------------------------------->
	if len(FikoYT.Istek.Dosyalar) > 0 {
		// YAP: Dosyaları işle ve GridFS'e kaydet
		for _, dosya := range FikoYT.Istek.Dosyalar {
			dosya.EnableEvents().
				AddEventHandler(FikoYTDosya.DefaultEventHandler).
				TumIslemleriYapEventli(fiber.Map{"yol": "./hareketler/" + db, "kid": FikoYT.AktifKullanici.Id}, nil)

			// YAP: Eğer koleksiyon goFirmalar ise ve dosya kaydedildiyse, logoId'yi güncelle
			if db == "goFirmalar" && dosya.Id != primitive.NilObjectID {
				fileID, _ := primitive.ObjectIDFromHex(dosya.Id.Hex())
				// Filter'dan _id'yi al
				if filterMap["_id"] != nil {
					_, err = koleksiyon.UpdateOne(ctx, filterMap, bson.M{"$set": bson.M{"logoId": fileID}})
					if err != nil {
						log.Printf("Logo ID güncelleme hatası: %v", err)
					}
				}
			}
		}
	}

	// Mail hesap guncelle hook
	if db == "hareketler" {
		mailHesapGuncelleHook(koleksiyon, filterMap)
	}

	// Paylaşım kartı canlı yayın (Socket.IO)
	if db == "hareketler" && result != nil && (result.ModifiedCount > 0 || result.UpsertedCount > 0) && paylasimVtMi(filterMap) {
		if kartId := objectIdToHex(filterMap["_id"]); kartId != "" {
			tip := "guncellendi"
			if s, ok := updateMap["silindi"].(bool); ok && s {
				tip = "silindi"
			}
			paylasimKartYayinla(FikoYT, tip, kartId)
		}
	}

	FikoYT.Cevap.Sonuc = result
	return c.JSON(result)
}

// ================== DOSYA YÖNETİM API'LERİ ==================

func fikoyt_sistem_dosya_yukle(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	koleksiyon := c.Params("db")

	// Kullanıcı ID'sini güvenli şekilde al
	var kullaniciId interface{} = nil
	if FikoYT.AktifKullanici != nil {
		kullaniciId = FikoYT.AktifKullanici.Id
	}

	// Gelen JSON direkt metadata olarak kullanılacak
	metadata := FikoYT.Istek.Json

	// Koleksiyon bilgisini metadata'ya ekle
	metadata["koleksiyon"] = koleksiyon
	metadata["kid"] = kullaniciId
	metadata["yol"] = "./hareketler/" + koleksiyon

	// Debug: metadata içeriğini logla
	log.Printf("📦 Gelen metadata: %+v", metadata)

	var sonuclar []interface{}

	for _, dosya := range FikoYT.Istek.Dosyalar {
		dosya.Id = primitive.NewObjectID()
		dosya.Kaydet()

		// Dosya bilgisini metadata'ya ekle (eğer yoksa)
		if _, exists := metadata["dosya"]; !exists {
			metadata["dosya"] = FikoYTMap.FikoYTMap{
				"adi":    dosya.File.Filename,
				"boyut":  dosya.File.Size,
				"uzanti": filepath.Ext(dosya.File.Filename),
			}
		}

		// Arka plan işlemi
		go func(d FikoYTDosya.FikoYTDosya, meta FikoYTMap.FikoYTMap) {
			d.EnableEvents().
				AddEventHandler(func(event *FikoYTDosya.ProcessEvent) error {
					if event.Type == FikoYTDosya.EventError {
						log.Printf("[HATA] [%s] %v", event.FileID.Hex(), event.Error)
					} else if event.Type == FikoYTDosya.EventProgress && event.ProcessStep == "COMPRESSION" {
						log.Printf("[ILERLEME] [%s] %.1f%%", event.FileID.Hex(), event.Progress)
					}
					return nil
				}).
				IsleEventli()

			// GridFS'e kaydet - metadata direkt kullanılıyor
			d.KaydetGridFsEventli(fiber.Map(meta))
			d.KaydetThumbGridFsEventli(fiber.Map{
				"yol":               meta["yol"],
				"koleksiyon":        koleksiyon,
				"kid":               meta["kid"],
				"thumb":             true,
				"file_id":           d.Id,
				"parent_file_id":    d.Id,
				"parent_collection": koleksiyon,
			})
			d.TemizleEventli()
		}(dosya, metadata)

		sonuc := map[string]interface{}{
			"id":         dosya.Id.Hex(),
			"dosya_adi":  dosya.File.Filename,
			"boyut":      dosya.File.Size,
			"mime_type":  "isleniyor...",
			"durum":      "yuklendi",
			"koleksiyon": koleksiyon,
			"kullanici":  fmt.Sprintf("%v", kullaniciId),
			"metadata":   metadata,
		}

		sonuclar = append(sonuclar, sonuc)
	}

	FikoYT.Cevap.Sonuc = map[string]interface{}{
		"basarili":   true,
		"mesaj":      fmt.Sprintf("✅ %d dosya %s koleksiyonuna yüklendi", len(FikoYT.Istek.Dosyalar), koleksiyon),
		"sonuclar":   sonuclar,
		"koleksiyon": koleksiyon,
		"islem_turu": "dosya_yukleme",
		"otomatik":   true,
	}

	return c.JSON(FikoYT.Cevap.Sonuc)
}

func fikoyt_sistem_dosya_bilgi(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	fileIDStr := c.Params("fileId")

	fileID, err := primitive.ObjectIDFromHex(fileIDStr)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"hata": "Geçersiz file ID"})
	}

	// 🎯 Dosyanın koleksiyonunu metadata'dan al veya tüm koleksiyonlarda ara
	ctx := context.TODO()
	db := FikoYT.Koleksiyon("go_files").Database()

	// Olası koleksiyon listesi (tüm koleksiyonlarda ara)
	collections := []string{"go_files", "goFirmalar", "urunler", "kullanicilar", "hareketler", "logolar", "dokumanlar"}

	var fileDoc bson.M

	// Tüm koleksiyonlarda ara
	for _, coll := range collections {
		err = db.Collection(coll+".files").FindOne(ctx, bson.M{"_id": fileID}).Decode(&fileDoc)
		if err == nil {
			break // bulundu
		}
	}

	// 🚀 Hala bulunamadıysa dosya henüz işleniyor olabilir
	if err != nil {
		// Geçici cevap - dosya işleniyor olabilir
		return c.JSON(map[string]interface{}{
			"id":           fileID.Hex(),
			"durum":        "isleniyor",
			"mesaj":        "⏳ Dosya hala işleniyor, lütfen bekleyin...",
			"yukleniyor":   true,
			"islem_durumu": "UPLOADING",
			"timestamp":    time.Now(),
			"not":          "Dosya yüklemesi başlatıldı ama henüz GridFS'e kaydedilmedi",
		})
	}

	// ✅ Dosya bulundu - bilgileri formatla
	bilgi := map[string]interface{}{
		"id":             fileID,
		"dosya_adi":      fileDoc["filename"],
		"boyut":          fileDoc["length"],
		"yukleme_tarihi": fileDoc["uploadDate"],
		"content_type":   fileDoc["contentType"],
		"metadata":       fileDoc["metadata"],
		"durum":          "hazir",
		"yuklendi":       true,
		"islem_durumu":   "COMPLETED",
	}

	// Thumbnail kontrolü
	if metadata, ok := fileDoc["metadata"].(bson.M); ok {
		if thumbID, exists := metadata["thumb_id"]; exists {
			bilgi["thumb_id"] = thumbID
			bilgi["thumb_var"] = true
		} else {
			bilgi["thumb_var"] = false
		}

		// Ek metadata bilgileri
		if koleksiyon, exists := metadata["koleksiyon"]; exists {
			bilgi["koleksiyon"] = koleksiyon
		}
		if kullaniciID, exists := metadata["kullanici_id"]; exists {
			bilgi["kullanici_id"] = kullaniciID
		}
	}

	FikoYT.Cevap.Sonuc = bilgi
	return c.JSON(FikoYT.Cevap.Sonuc)
}

// ================== MAIL IMAP KLASÖR LİSTELEME ==================

func fikoyt_sistem_mail_klasorler(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	FikoYT.Istek.Json.IsleObjectId()

	host, _ := FikoYT.Istek.Json["host"].(string)
	email, _ := FikoYT.Istek.Json["email"].(string)
	sifre, _ := FikoYT.Istek.Json["sifre"].(string)
	guvenlik, _ := FikoYT.Istek.Json["guvenlik"].(string)

	var port int
	switch v := FikoYT.Istek.Json["port"].(type) {
	case float64:
		port = int(v)
	case int:
		port = v
	default:
		port = 993
	}

	if host == "" || email == "" || sifre == "" {
		FikoYT.Cevap.Sonuc = fiber.Map{"hata": "host, email ve sifre zorunlu", "klasorler": []string{}}
		return c.JSON(FikoYT.Cevap.Sonuc)
	}

	klasorler, err := mail_dinleyici.KlasorleriListele(host, port, email, sifre, guvenlik)
	if err != nil {
		FikoYT.Cevap.Sonuc = fiber.Map{"hata": err.Error(), "klasorler": []string{}}
		return c.JSON(FikoYT.Cevap.Sonuc)
	}

	FikoYT.Cevap.Sonuc = fiber.Map{"klasorler": klasorler}
	return c.JSON(FikoYT.Cevap.Sonuc)
}

func fikoyt_sistem_mail_yeniden_cek(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	FikoYT.Istek.Json.IsleObjectId()

	hesapId := ""
	switch v := FikoYT.Istek.Json["hesapId"].(type) {
	case primitive.ObjectID:
		hesapId = v.Hex()
	case string:
		hesapId = v
	}
	if hesapId == "" {
		FikoYT.Cevap.Sonuc = fiber.Map{"hata": "hesapId zorunlu"}
		return c.JSON(FikoYT.Cevap.Sonuc)
	}

	silMod, _ := FikoYT.Istek.Json["sil"].(bool)
	var silinen int64
	var err error
	if silMod {
		silinen, err = mail_dinleyici.SilVeYenidenCekHesap(hesapId)
	} else {
		silinen, err = mail_dinleyici.YenidenCekHesap(hesapId)
		// Eksik body'leri de tamamla
		go mail_dinleyici.EksikBodyleriTamamla(hesapId)
	}
	if err != nil {
		FikoYT.Cevap.Sonuc = fiber.Map{"hata": err.Error()}
		return c.JSON(FikoYT.Cevap.Sonuc)
	}

	FikoYT.Cevap.Sonuc = fiber.Map{"basarili": true, "silinenMail": silinen}
	return c.JSON(FikoYT.Cevap.Sonuc)
}

func fikoyt_sistem_mail_iptal(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	FikoYT.Istek.Json.IsleObjectId()

	hesapId := ""
	switch v := FikoYT.Istek.Json["hesapId"].(type) {
	case primitive.ObjectID:
		hesapId = v.Hex()
	case string:
		hesapId = v
	}
	if hesapId == "" {
		FikoYT.Cevap.Sonuc = fiber.Map{"hata": "hesapId zorunlu"}
		return c.JSON(FikoYT.Cevap.Sonuc)
	}

	mail_dinleyici.DurdurHesap(hesapId)
	// Yeniden başlat (sadece IDLE dinleme, arka plan işlemleri iptal)
	go mail_dinleyici.YenidenBaslatHesap(hesapId)

	FikoYT.Cevap.Sonuc = fiber.Map{"basarili": true}
	return c.JSON(FikoYT.Cevap.Sonuc)
}

func fikoyt_sistem_mail_eski_cek(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	FikoYT.Istek.Json.IsleObjectId()

	hesapId := ""
	switch v := FikoYT.Istek.Json["hesapId"].(type) {
	case primitive.ObjectID:
		hesapId = v.Hex()
	case string:
		hesapId = v
	}
	if hesapId == "" {
		FikoYT.Cevap.Sonuc = fiber.Map{"hata": "hesapId zorunlu"}
		return c.JSON(FikoYT.Cevap.Sonuc)
	}

	adet := 50
	switch v := FikoYT.Istek.Json["adet"].(type) {
	case float64:
		adet = int(v)
	case int:
		adet = v
	}

	go func() {
		sayac, _ := mail_dinleyici.EskiMailleriCek(hesapId, adet)
		log.Printf("[kokDB] Eski mail cekme tamamlandi: %d mail", sayac)
	}()

	FikoYT.Cevap.Sonuc = fiber.Map{"basarili": true, "adet": adet}
	return c.JSON(FikoYT.Cevap.Sonuc)
}

// EML dosyası import et
func fikoyt_sistem_mail_eml_import(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)

	// hesapId al
	hesapIdStr := ""
	if FikoYT.Istek.Json != nil {
		FikoYT.Istek.Json.IsleObjectId()
		switch v := FikoYT.Istek.Json["hesapId"].(type) {
		case primitive.ObjectID:
			hesapIdStr = v.Hex()
		case string:
			hesapIdStr = v
		}
	}

	hesapOid, err := primitive.ObjectIDFromHex(hesapIdStr)
	if err != nil {
		FikoYT.Cevap.Sonuc = fiber.Map{"hata": "gecersiz hesapId"}
		return c.JSON(FikoYT.Cevap.Sonuc)
	}

	// Hesap bilgilerini al
	koleksiyon := FikoYT.Koleksiyon("hareketler")
	var hesap bson.M
	koleksiyon.FindOne(context.TODO(), bson.M{"_id": hesapOid, "vt": "FikoYT.sistem.mail.hesaplar"}).Decode(&hesap)
	if hesap == nil {
		FikoYT.Cevap.Sonuc = fiber.Map{"hata": "hesap bulunamadi"}
		return c.JSON(FikoYT.Cevap.Sonuc)
	}
	firmaId, _ := hesap["firmaId"].(string)

	// Dosyaları al
	if len(FikoYT.Istek.Dosyalar) == 0 {
		FikoYT.Cevap.Sonuc = fiber.Map{"hata": "dosya yuklenemedi"}
		return c.JSON(FikoYT.Cevap.Sonuc)
	}

	yuklenen := 0
	for _, dosya := range FikoYT.Istek.Dosyalar {
		dosya.Kaydet()
		// EML dosyasını oku
		f, openErr := dosya.File.Open()
		if openErr != nil {
			continue
		}
		raw, readErr := io.ReadAll(f)
		f.Close()
		if readErr != nil || len(raw) == 0 {
			continue
		}

		// MSG (OLE2) format kontrolü — desteklenmiyor
		if len(raw) >= 8 && raw[0] == 0xD0 && raw[1] == 0xCF && raw[2] == 0x11 && raw[3] == 0xE0 && raw[4] == 0xA1 && raw[5] == 0xB1 && raw[6] == 0x1A && raw[7] == 0xE1 {
			FikoYT.Cevap.Sonuc = fiber.Map{"hata": "MSG (Outlook) formatı desteklenmiyor. Lütfen dosyayı EML formatında kaydedin."}
			return c.JSON(FikoYT.Cevap.Sonuc)
		}

		// EML format doğrulaması — bilinen MIME başlıkları kontrolü
		rawStart := string(raw[:min(len(raw), 4096)])
		rawStartLower := strings.ToLower(rawStart)
		emlGecerli := false
		emlBasliklar := []string{"from:", "subject:", "date:", "mime-version:", "message-id:", "received:", "content-type:", "return-path:", "delivered-to:"}
		for _, baslik := range emlBasliklar {
			if strings.Contains(rawStartLower, baslik) {
				emlGecerli = true
				break
			}
		}
		if !emlGecerli {
			FikoYT.Cevap.Sonuc = fiber.Map{"hata": "Geçersiz dosya formatı. Dosya EML (e-posta) formatında değil."}
			return c.JSON(FikoYT.Cevap.Sonuc)
		}

		// Parse et
		text, html, ekler := mail_dinleyici.ParseMIMEBodyPublic(raw)

		// Envelope bilgilerini raw'dan çıkar
		kimden := mail_dinleyici.ExtractRawHeader(raw, "From")
		kime := mail_dinleyici.ExtractRawHeader(raw, "To")
		konu := mail_dinleyici.ExtractRawHeader(raw, "Subject")
		tarihStr := mail_dinleyici.ExtractRawHeader(raw, "Date")
		messageId := mail_dinleyici.ExtractRawHeader(raw, "Message-ID")
		inReplyTo := mail_dinleyici.ExtractRawHeader(raw, "In-Reply-To")

		// Duplicate kontrolü
		if messageId != "" {
			count, _ := koleksiyon.CountDocuments(context.TODO(), bson.M{
				"vt": "FikoYT.sistem.mail.mesajlar", "veri.messageId": messageId, "sahip": hesapOid,
			})
			if count > 0 {
				continue
			}
		}

		// Ekleri GridFS'e kaydet
		var eklerBson []bson.M
		// TODO: Ekleri kaydet

		kayit := bson.M{
			"vt":                "FikoYT.sistem.mail.mesajlar",
			"tarih":             tarihStr,
			"olusturulmaTarihi": time.Now(),
			"turu":              "VERI",
			"sahip":             hesapOid,
			"gizlilik":          "Uygulama",
			"veri": bson.M{
				"kimden":    kimden,
				"kime":      kime,
				"konu":      konu,
				"messageId": messageId,
				"klasor":    "import",
				"okundu":    true,
				"okunduBy":  bson.A{},
				"hesapId":   hesapOid,
				"firmaId":   firmaId,
				"inReplyTo": inReplyTo,
			},
			"icerikler": bson.A{bson.M{
				"html":     html,
				"aciklama": text,
			}},
			"dosyalar":  eklerBson,
			"etiketler": bson.A{bson.M{"degeri": "import"}},
		}
		_ = ekler // TODO: ekleri kullan

		_, insertErr := koleksiyon.InsertOne(context.TODO(), kayit)
		if insertErr == nil {
			yuklenen++
		}
	}

	FikoYT.Cevap.Sonuc = fiber.Map{"basarili": true, "yuklenen": yuklenen}
	return c.JSON(FikoYT.Cevap.Sonuc)
}

// EML dosyası olarak indir - ekler dahil
func fikoyt_sistem_mail_eml_indir(c fiber.Ctx) error {
	mailIdStr := c.Params("mailId")
	mailId, err := primitive.ObjectIDFromHex(mailIdStr)
	if err != nil {
		return c.Status(400).SendString("Gecersiz mail ID")
	}

	ctx := context.TODO()
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	koleksiyon := FikoYT.Koleksiyon("hareketler")

	var mail bson.M
	koleksiyon.FindOne(ctx, bson.M{"_id": mailId, "vt": "FikoYT.sistem.mail.mesajlar"}).Decode(&mail)
	if mail == nil {
		return c.Status(404).SendString("Mail bulunamadi")
	}

	kimden, _ := mail["kimden"].(string)
	kime, _ := mail["kime"].(string)
	konu, _ := mail["konu"].(string)
	icerik, _ := mail["icerik"].(string)
	icerikHtml, _ := mail["icerikHtml"].(string)
	tarih, _ := mail["tarih"].(primitive.DateTime)

	boundary := fmt.Sprintf("----=_FikoYT_%d", time.Now().UnixNano())

	var eml strings.Builder
	eml.WriteString("MIME-Version: 1.0\r\n")
	eml.WriteString(fmt.Sprintf("Date: %s\r\n", time.Unix(int64(tarih)/1000, 0).UTC().Format(time.RFC1123Z)))
	eml.WriteString(fmt.Sprintf("From: %s\r\n", kimden))
	eml.WriteString(fmt.Sprintf("To: %s\r\n", kime))
	eml.WriteString(fmt.Sprintf("Subject: =?UTF-8?B?%s?=\r\n", base64.StdEncoding.EncodeToString([]byte(konu))))

	// Ekler var mı?
	ekler, _ := mail["ekler"].(primitive.A)
	ekliMi := len(ekler) > 0

	if ekliMi {
		eml.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=\"%s\"\r\n\r\n", boundary))

		// Body part (multipart/alternative)
		altBoundary := boundary + "_alt"
		eml.WriteString(fmt.Sprintf("--%s\r\n", boundary))
		eml.WriteString(fmt.Sprintf("Content-Type: multipart/alternative; boundary=\"%s\"\r\n\r\n", altBoundary))

		if icerik != "" {
			eml.WriteString(fmt.Sprintf("--%s\r\n", altBoundary))
			eml.WriteString("Content-Type: text/plain; charset=UTF-8\r\n")
			eml.WriteString("Content-Transfer-Encoding: base64\r\n\r\n")
			eml.WriteString(base64.StdEncoding.EncodeToString([]byte(icerik)))
			eml.WriteString("\r\n")
		}
		if icerikHtml != "" {
			eml.WriteString(fmt.Sprintf("--%s\r\n", altBoundary))
			eml.WriteString("Content-Type: text/html; charset=UTF-8\r\n")
			eml.WriteString("Content-Transfer-Encoding: base64\r\n\r\n")
			eml.WriteString(base64.StdEncoding.EncodeToString([]byte(icerikHtml)))
			eml.WriteString("\r\n")
		}
		eml.WriteString(fmt.Sprintf("--%s--\r\n", altBoundary))

		// Ek dosyalar
		db := koleksiyon.Database()
		bucket, bucketErr := mail_dinleyici.GridFSBucket(db)
		if bucketErr == nil {
			for _, ekRaw := range ekler {
				ek, ok := ekRaw.(bson.M)
				if !ok {
					continue
				}
				dosyaAdi, _ := ek["dosyaAdi"].(string)
				tur, _ := ek["tur"].(string)
				dosyaId, ok := ek["dosyaId"].(primitive.ObjectID)
				if !ok || dosyaAdi == "" {
					continue
				}
				if tur == "" {
					tur = "application/octet-stream"
				}

				// GridFS'ten oku
				stream, dlErr := bucket.OpenDownloadStream(dosyaId)
				if dlErr != nil {
					continue
				}
				data, readErr := io.ReadAll(stream)
				stream.Close()
				if readErr != nil || len(data) == 0 {
					continue
				}

				eml.WriteString(fmt.Sprintf("\r\n--%s\r\n", boundary))
				eml.WriteString(fmt.Sprintf("Content-Type: %s; name=\"%s\"\r\n", tur, dosyaAdi))
				eml.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=\"%s\"\r\n", dosyaAdi))
				eml.WriteString("Content-Transfer-Encoding: base64\r\n\r\n")

				// Base64 encode - 76 karakter satır
				b64 := base64.StdEncoding.EncodeToString(data)
				for i := 0; i < len(b64); i += 76 {
					end := i + 76
					if end > len(b64) {
						end = len(b64)
					}
					eml.WriteString(b64[i:end])
					eml.WriteString("\r\n")
				}
			}
		}
		eml.WriteString(fmt.Sprintf("--%s--\r\n", boundary))
	} else {
		// Ek yok - sadece body
		if icerikHtml != "" {
			eml.WriteString("Content-Type: text/html; charset=UTF-8\r\n")
			eml.WriteString("Content-Transfer-Encoding: base64\r\n\r\n")
			eml.WriteString(base64.StdEncoding.EncodeToString([]byte(icerikHtml)))
		} else {
			eml.WriteString("Content-Type: text/plain; charset=UTF-8\r\n")
			eml.WriteString("Content-Transfer-Encoding: base64\r\n\r\n")
			eml.WriteString(base64.StdEncoding.EncodeToString([]byte(icerik)))
		}
	}

	// Dosya adı
	safeKonu := konu
	if len(safeKonu) > 50 {
		safeKonu = safeKonu[:50]
	}

	c.Set("Content-Type", "message/rfc822")
	c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.eml\"", safeKonu))
	return c.SendString(eml.String())
}
