package kokDB

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"server/sistem/FikoYTDosya"
	FikoYTCtx "server/sistem/fikoyt_ctx"
	FikoYTMap "server/sistem/fikoyt_map"
	"strings"
	"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"
)

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")

	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{})
	}

	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")

	koleksiyon := FikoYT.Koleksiyon(db)

	// var dbMap FikoYTMap.FikoYTMap
	// ctx := context.TODO()

	FikoYT.Istek.Json.IsleObjectId()
	// koleksiyon.CountDocuments(context.TODO(), FikoYTMap.FikoYTMap{})
	//------------------------------------->

	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})
		for _, dosya := range FikoYT.Istek.Dosyalar {
			dosya.Kaydet()
			go func(d FikoYTDosya.FikoYTDosya) {
				d.EnableEvents().
					AddEventHandler(func(event *FikoYTDosya.ProcessEvent) error {
						// 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

	return c.JSON(sonuclar)
}

func fikoyt_sistem_db_sil(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	db := c.Params("db")

	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

	switch FikoYT.Istek.JsonTip {
	case "Object":
		sonuc, err = koleksiyon.DeleteMany(ctx, FikoYT.Istek.Json)
	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()
				if r, e := koleksiyon.DeleteMany(ctx, m); 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)
	}

	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")
	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()

	// 3. upsert ayarı (varsayılan true)
	upsert := true
	if u, ok := FikoYT.Istek.Json["upsert"].(bool); ok {
		upsert = u
	}
	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, filterMap, updateDoc, opts)
	} else {
		result, err = koleksiyon.UpdateOne(ctx, filterMap, 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)
					}
				}
			}
		}
	}

	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") // URL'den koleksiyon adı!

	// Kullanıcı ID'sini güvenli şekilde al
	var kullaniciId interface{} = nil
	if FikoYT.AktifKullanici != nil {
		kullaniciId = FikoYT.AktifKullanici.Id
	}

	// 🎯 Sihirli kısım: JSON verisi varsa metadata olarak kaydet
	metadata := fiber.Map{
		"yol":            "./hareketler/" + koleksiyon,
		"kid":            kullaniciId,
		"kullanici_id":   kullaniciId,
		"koleksiyon":     koleksiyon,
		"yukleme_tarihi": time.Now(),
	}

	// JSON'dan gelen tüm verileri metadata'ya ekle (otomatik!)
	if FikoYT.Istek.Json != nil {
		for key, value := range FikoYT.Istek.Json {
			// if key != "file" && key != "dosya" { // dosya field'larını atla
			metadata[key] = value
			// }
		}
	}

	// 📋 req_body'den metadata'yı parse et (FikoYT standart pattern)
	if c.Request().Header.ContentType() != nil {
		contentType := string(c.Request().Header.ContentType())
		if strings.Contains(contentType, "multipart/form-data") {
			// req_body field'ını al ve parse et
			reqBodyStr := c.FormValue("req_body")
			if reqBodyStr != "" {
				var reqBodyData map[string]interface{}
				err := json.Unmarshal([]byte(reqBodyStr), &reqBodyData)
				if err == nil {
					log.Printf("📦 req_body parsed: %+v", reqBodyData)
					// req_body'deki tüm field'ları metadata'ya ekle (nested yapı korunur)
					for key, value := range reqBodyData {
						metadata[key] = value
					}
					log.Printf("📊 Final metadata: %+v", metadata)
				} else {
					log.Printf("❌ req_body parse error: %v", err)
				}
			}
		}
	}

	var sonuclar []interface{}

	for _, dosya := range FikoYT.Istek.Dosyalar {
		// Yeni ObjectID oluştur
		dosya.Id = primitive.NewObjectID()

		// Dosyayı kaydet (ID ile birlikte)
		dosya.Kaydet()

		// Metadata'ya koleksiyon adını GridFS için ekle
		metadataWithCollection := fiber.Map{}
		for k, v := range metadata {
			metadataWithCollection[k] = v
		}
		metadataWithCollection["gridfs_collection"] = koleksiyon // GridFS koleksiyon adı

		// 🚀 Akıllı arka plan işlem
		go func(d FikoYTDosya.FikoYTDosya, meta fiber.Map, kol string) {
			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 doğru koleksiyonda kaydet
			// yol parametresini koleksiyon adı ile değiştir
			gridFsMeta := fiber.Map{}
			for k, v := range meta {
				gridFsMeta[k] = v
			}
			// meta zaten "yol" ve "koleksiyon" parametrelerini içeriyor
			// saveToGridFS fonksiyonu "koleksiyon" parametresini bucket name olarak kullanacak

			d.KaydetGridFsEventli(gridFsMeta)
			d.KaydetThumbGridFsEventli(fiber.Map{
				"yol":               meta["yol"],
				"koleksiyon":        kol,
				"kid":               meta["kid"],
				"thumb":             true,
				"file_id":           d.Id,
				"parent_file_id":    d.Id,
				"parent_collection": kol,
			})
			d.TemizleEventli()
		}(dosya, metadataWithCollection, koleksiyon)

		// ✨ Zengin sonuc formatı
		sonuc := map[string]interface{}{
			"id":         dosya.Id.Hex(),
			"dosya_adi":  dosya.File.Filename,
			"boyut":      dosya.File.Size,
			"mime_type":  "isleniyor...", // sonradan güncellenecek
			"durum":      "yuklendi",
			"koleksiyon": koleksiyon,
			"kullanici":  fmt.Sprintf("%v", kullaniciId),
			"metadata":   metadata, // gönderilen tüm metadata
		}

		sonuclar = append(sonuclar, sonuc)
	}

	// 🎯 FikoYT pattern'ine uygun response
	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)
}
