package kokDB

import (
	"context"
	"fmt"
	"log"
	"server/sistem/FikoYTDosya"
	FikoYTCtx "server/sistem/fikoyt_ctx"
	FikoYTMap "server/sistem/fikoyt_map"
	fiko_mongo "server/sistem/fikoyt_mongo"
	"strconv"
	"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/gridfs"
	"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ÜKLEME API'LERİ ==================

func fikoyt_sistem_dosya_yukle(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)

	// Formdan gelen verileri al
	koleksiyon := c.FormValue("koleksiyon")
	if koleksiyon == "" {
		koleksiyon = "go_files" // default
	}

	// Metadata (opsiyonel)
	metadata := fiber.Map{
		"yol":            "./hareketler/" + koleksiyon,
		"kid":            FikoYT.AktifKullanici.Id,
		"kullanici_id":   FikoYT.AktifKullanici.Id,
		"koleksiyon":     koleksiyon,
		"yukleme_tarihi": time.Now(),
	}

	// Ek metadata alanları
	if aciklama := c.FormValue("aciklama"); aciklama != "" {
		metadata["aciklama"] = aciklama
	}
	if etiket := c.FormValue("etiket"); etiket != "" {
		metadata["etiket"] = etiket
	}

	// Dosyaları işle
	var sonuclar []interface{}

	for _, dosya := range FikoYT.Istek.Dosyalar {
		// Dosyayı otomatik işle (kaydet + işle + GridFS'e kaydet + temizle)
		dosya.EnableEvents().
			AddEventHandler(func(event *FikoYTDosya.ProcessEvent) error {
				if event.Type == FikoYTDosya.EventError {
					log.Printf("Dosya işleme hatası [%s]: %v", event.FileID.Hex(), event.Error)
				} else if event.Type == FikoYTDosya.EventProgress && event.ProcessStep == "COMPRESSION" {
					log.Printf("Sıkıştırma ilerlemesi [%s]: %.3f%%", event.FileID.Hex(), event.Progress)
				}
				return nil
			}).
			TumIslemleriYapEventli(metadata, fiber.Map{"yol": "./hareketler/" + koleksiyon, "kid": FikoYT.AktifKullanici.Id, "thumb": true, "file_id": dosya.Id})

		// Sonucu ekle
		sonuc := map[string]interface{}{
			"id":             dosya.Id,
			"file_id":        dosya.Id.Hex(),
			"dosya_adi":      dosya.File.Filename,
			"boyut":          dosya.File.Size,
			"mime_type":      dosya.MimeType,
			"islenmis":       dosya.Processed,
			"thumb_var":      dosya.HasThumbnail(),
			"thumb_id":       dosya.ThumbId.Hex(),
			"yukleme_zamani": time.Now(),
		}

		if dosya.HasThumbnail() {
			sonuc["thumb_id"] = dosya.ThumbId.Hex()
		}

		sonuclar = append(sonuclar, sonuc)
	}

	// Cevap hazırla
	cevap := map[string]interface{}{
		"basarili":     true,
		"mesaj":        fmt.Sprintf("%d dosya başarıyla yüklendi", len(FikoYT.Istek.Dosyalar)),
		"sonuclar":     sonuclar,
		"kullanici_id": FikoYT.AktifKullanici.Id,
		"koleksiyon":   koleksiyon,
		"tarih":        time.Now(),
	}

	FikoYT.Cevap.Sonuc = cevap
	return c.JSON(cevap)
}

func fikoyt_sistem_dosya_batch_yukle(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)

	koleksiyon := c.FormValue("koleksiyon")
	if koleksiyon == "" {
		koleksiyon = "go_files"
	}

	maxConcurrent := 3
	if max := c.FormValue("max_concurrent"); max != "" {
		if m, err := strconv.Atoi(max); err == nil && m > 0 {
			maxConcurrent = m
		}
	}

	metadata := fiber.Map{
		"yol":            "./hareketler/" + koleksiyon,
		"kid":            FikoYT.AktifKullanici.Id,
		"kullanici_id":   FikoYT.AktifKullanici.Id,
		"koleksiyon":     koleksiyon,
		"yukleme_tarihi": time.Now(),
		"batch_upload":   true,
	}

	// Batch processor oluştur
	batchProcessor := FikoYTDosya.NewBatchProcessor(FikoYT.Istek.Dosyalar)

	// Event handler ekle
	batchProcessor.AddEventHandler(func(event *FikoYTDosya.ProcessEvent) error {
		if event.Type == FikoYTDosya.EventError {
			log.Printf("Batch işleme hatası: %v", event.Error)
		}
		return nil
	})

	// Batch işlemini başlat
	go func() {
		batchProcessor.ProcessAllEventli(metadata,
			fiber.Map{"yol": "./hareketler/" + koleksiyon, "kid": FikoYT.AktifKullanici.Id, "thumb": true},
			maxConcurrent)
	}()

	// Anında cevap dön
	cevap := map[string]interface{}{
		"basarili":     true,
		"mesaj":        fmt.Sprintf("Batch yükleme başlatıldı: %d dosya, max %d concurrent", len(FikoYT.Istek.Dosyalar), maxConcurrent),
		"batch_id":     batchProcessor.BatchID,
		"dosya_sayisi": len(FikoYT.Istek.Dosyalar),
		"koleksiyon":   koleksiyon,
		"tarih":        time.Now(),
	}

	FikoYT.Cevap.Sonuc = cevap
	return c.JSON(cevap)
}

func fikoyt_sistem_dosya_durum(c fiber.Ctx) error {
	fileIDStr := c.Params("fileId")
	fileID, err := primitive.ObjectIDFromHex(fileIDStr)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"hata": "Geçersiz file ID"})
	}

	// GridFS'den dosya bilgilerini al
	db := fiko_mongo.GetMongoDBClient().Database("fikoyt")
	fsName := "go_files"
	bucket, err := gridfs.NewBucket(db, &options.BucketOptions{Name: &fsName})
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"hata": "GridFS erişim hatası"})
	}

	var fileDoc bson.M
	err = db.Collection(fsName+".files").FindOne(context.TODO(), bson.M{"_id": fileID}).Decode(&fileDoc)
	if err != nil {
		return c.Status(404).JSON(fiber.Map{"hata": "Dosya bulunamadı"})
	}

	// Dosya bilgilerini hazırla
	cevap := map[string]interface{}{
		"id":             fileID,
		"dosya_adi":      fileDoc["filename"],
		"boyut":          fileDoc["length"],
		"yukleme_tarihi": fileDoc["uploadDate"],
		"mime_type":      fileDoc["contentType"],
		"metadata":       fileDoc["metadata"],
	}

	// Thumbnail var mı?
	if metadata, ok := fileDoc["metadata"].(bson.M); ok {
		if thumbID, exists := metadata["thumb_id"]; exists {
			cevap["thumb_id"] = thumbID
			cevap["thumb_var"] = true
		} else {
			cevap["thumb_var"] = false
		}
	}

	return c.JSON(cevap)
}
