package rag

import (
	"context"
	"fmt"
	"strings"
	"time"

	FikoYTCtx "server/sistem/fikoyt_ctx"
	fiko_mongo "server/sistem/fikoyt_mongo"

	"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"
)

const SohbetVT = "FikoYT.sistem.rag.sohbet"

var (
	sabitBaglamCache     string
	sabitBaglamCacheZaman time.Time
)

func sabitBaglamYukle() string {
	if time.Since(sabitBaglamCacheZaman) < 5*time.Minute && sabitBaglamCache != "" {
		return sabitBaglamCache
	}

	kol := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("hareketler")
	cursor, err := kol.Find(context.TODO(), bson.M{
		"vt":             "FikoYT.sistem.dokumantasyon",
		"veri.kategori":  "sabit-baglam",
	})
	if err != nil {
		return ""
	}
	defer cursor.Close(context.TODO())

	var sb strings.Builder
	for cursor.Next(context.TODO()) {
		var doc bson.M
		cursor.Decode(&doc)
		baslik := ""
		if v, ok := doc["veri"].(bson.M); ok {
			baslik, _ = v["baslik"].(string)
		}
		metin := ""
		if ic, ok := doc["icerikler"].(bson.A); ok && len(ic) > 0 {
			if ilk, ok := ic[0].(bson.M); ok {
				metin, _ = ilk["metin"].(string)
			}
		}
		if metin != "" {
			if baslik != "" {
				sb.WriteString(fmt.Sprintf("### %s\n", baslik))
			}
			sb.WriteString(metin)
			sb.WriteString("\n\n")
		}
	}

	sabitBaglamCache = sb.String()
	sabitBaglamCacheZaman = time.Now()
	return sabitBaglamCache
}

func kullaniciAl(c fiber.Ctx) (*FikoYTCtx.FikoYTCtx, error) {
	fkRaw := c.Locals("FikoYTCtx")
	if fkRaw == nil {
		return nil, c.Status(401).JSON(fiber.Map{"hata": "Oturum bulunamadı"})
	}
	fk, ok := fkRaw.(*FikoYTCtx.FikoYTCtx)
	if !ok || fk == nil || fk.AktifKullanici == nil {
		return nil, c.Status(401).JSON(fiber.Map{"hata": "Giriş yapmanız gerekiyor"})
	}
	return fk, nil
}

func sezonIdAl(fk *FikoYTCtx.FikoYTCtx) string {
	if fk.Sezon != nil {
		return fk.Sezon.ID()
	}
	return ""
}

func konusmaYeniHandler(c fiber.Ctx) error {
	fk, err := kullaniciAl(c)
	if err != nil {
		return err
	}

	kol := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("hareketler")

	kullaniciId := fk.AktifKullanici.IsleObjectId().Id
	isim := fmt.Sprintf("%v", fk.AktifKullanici.Isim)

	doc := bson.M{
		"vt": SohbetVT,
		"veri": bson.M{
			"baslik":          "Yeni Sohbet",
			"kullanici_id":    kullaniciId,
			"kullanici_ismi":  isim,
			"sezon_id":        sezonIdAl(fk),
		},
		"icerikler": bson.A{},
		"olusturma": time.Now(),
		"guncelleme": time.Now(),
	}

	result, err := kol.InsertOne(context.TODO(), doc)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"hata": err.Error()})
	}

	doc["_id"] = result.InsertedID
	return c.JSON(doc)
}

func konusmaListeHandler(c fiber.Ctx) error {
	fk, err := kullaniciAl(c)
	if err != nil {
		return err
	}

	kol := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("hareketler")
	kullaniciId := fk.AktifKullanici.IsleObjectId().Id

	opts := options.Find().
		SetSort(bson.M{"guncelleme": -1}).
		SetLimit(50).
		SetProjection(bson.M{
			"vt":         1,
			"veri":       1,
			"olusturma":  1,
			"guncelleme": 1,
			"icerikler":  bson.M{"$slice": -1},
		})

	filtre := bson.M{
		"vt":                SohbetVT,
		"veri.kullanici_id": kullaniciId,
	}
	sezon := sezonIdAl(fk)
	if sezon != "" {
		filtre["veri.sezon_id"] = sezon
	}

	cursor, err := kol.Find(context.TODO(), filtre, opts)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"hata": err.Error()})
	}

	var sonuclar []bson.M
	cursor.All(context.TODO(), &sonuclar)

	if sonuclar == nil {
		sonuclar = []bson.M{}
	}

	return c.JSON(sonuclar)
}

func konusmaGetirHandler(c fiber.Ctx) error {
	fk, err := kullaniciAl(c)
	if err != nil {
		return err
	}

	idStr := c.Params("id")
	oid, err := primitive.ObjectIDFromHex(idStr)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"hata": "Geçersiz ID"})
	}

	kol := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("hareketler")
	kullaniciId := fk.AktifKullanici.IsleObjectId().Id

	var doc bson.M
	err = kol.FindOne(context.TODO(), bson.M{
		"_id":               oid,
		"vt":                SohbetVT,
		"veri.kullanici_id": kullaniciId,
	}).Decode(&doc)

	if err != nil {
		return c.Status(404).JSON(fiber.Map{"hata": "Sohbet bulunamadı"})
	}

	return c.JSON(doc)
}

func konusmaMesajHandler(c fiber.Ctx) error {
	baglam, err := sohbetBaglamiHazirla(c)
	if err != nil {
		return err
	}

	rateLimitAl(baglam.KullaniciIdStr)
	defer rateLimitBirak(baglam.KullaniciIdStr)

	ChatBasladi()
	defer ChatBitti()

	cevapMetin, err := ollamaChatRetry(baglam.Mesajlar, 2)
	if err != nil {
		cevapMetin = "Üzgünüm, şu anda yanıt üretemiyorum: " + err.Error()
	}

	asistanMesaj := bson.M{
		"rol":          "asistan",
		"metin":        cevapMetin,
		"zaman":        time.Now(),
		"kaynakSayisi": len(baglam.RagSonuclar),
	}

	kol := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("hareketler")
	kol.UpdateOne(context.TODO(),
		bson.M{"_id": baglam.KonusmaId},
		bson.M{
			"$push": bson.M{
				"icerikler": bson.M{"$each": bson.A{baglam.KullaniciMesaj, asistanMesaj}},
			},
			"$set": bson.M{"guncelleme": time.Now()},
		},
	)

	return c.JSON(fiber.Map{
		"konusma_id":   baglam.KonusmaId.Hex(),
		"cevap":        cevapMetin,
		"kaynakSayisi": len(baglam.RagSonuclar),
		"kaynakIdler":  baglam.KaynakIdler,
	})
}

func sistemIstatistikBaglami() string {
	ctx := context.TODO()
	db := fiko_mongo.GetMongoDBClient().Database("fikoyt")

	var sb strings.Builder

	toplamHareket, _ := db.Collection("hareketler").CountDocuments(ctx, bson.M{})
	toplamUye, _ := db.Collection("goUyeler").CountDocuments(ctx, bson.M{})
	toplamCihaz, _ := db.Collection("goCihazlar").CountDocuments(ctx, bson.M{})
	aktifSoket, _ := db.Collection("goSoketler").CountDocuments(ctx, bson.M{})
	toplamGomme, _ := db.Collection(GommeKoleksiyon).CountDocuments(ctx, bson.M{})

	sb.WriteString(fmt.Sprintf("Toplam kayıt (hareketler): %d\n", toplamHareket))
	sb.WriteString(fmt.Sprintf("Toplam kullanıcı: %d\n", toplamUye))
	sb.WriteString(fmt.Sprintf("Toplam cihaz: %d\n", toplamCihaz))
	sb.WriteString(fmt.Sprintf("Aktif bağlantı: %d\n", aktifSoket))
	sb.WriteString(fmt.Sprintf("Embed edilmiş kayıt: %d\n", toplamGomme))

	// VT dağılımı (top 10)
	pipeline := []bson.M{
		{"$group": bson.M{"_id": "$vt", "sayi": bson.M{"$sum": 1}}},
		{"$sort": bson.M{"sayi": -1}},
		{"$limit": 15},
	}

	cursor, err := db.Collection("hareketler").Aggregate(ctx, pipeline)
	if err == nil {
		var vtler []bson.M
		cursor.All(ctx, &vtler)
		sb.WriteString("\nVT Dağılımı:\n")
		for _, vt := range vtler {
			vtIsim, _ := vt["_id"].(string)
			sayi, _ := vt["sayi"].(int32)
			if vtIsim != "" {
				sb.WriteString(fmt.Sprintf("- %s: %d kayıt\n", vtIsim, sayi))
			}
		}
	}

	return sb.String()
}

func toHex(val interface{}) string {
	switch v := val.(type) {
	case primitive.ObjectID:
		return v.Hex()
	case string:
		return v
	default:
		if val == nil {
			return ""
		}
		return fmt.Sprintf("%v", val)
	}
}

func tersIliskiCikar(tur string, digerKisiCinsiyet string) string {
	switch tur {
	case "COCUK":
		if digerKisiCinsiyet == "E" {
			return "BABA"
		}
		return "ANNE"
	case "BABA":
		return "COCUK"
	case "ANNE":
		return "COCUK"
	case "ES":
		return "ES"
	case "KARDES":
		return "KARDES"
	case "YEGEN":
		if digerKisiCinsiyet == "E" {
			return "AMCA"
		}
		return "HALA"
	case "AMCA":
		return "YEGEN"
	case "HALA":
		return "YEGEN"
	case "TEYZE":
		return "YEGEN"
	case "DAYI":
		return "YEGEN"
	case "TORUN":
		if digerKisiCinsiyet == "E" {
			return "DEDE"
		}
		return "BABAANNE"
	case "DEDE":
		return "TORUN"
	case "BABAANNE", "ANNEANNE":
		return "TORUN"
	case "KUZEN":
		return "KUZEN"
	default:
		return tur
	}
}

func turInsanOkunur(tur string) string {
	switch tur {
	case "BABA":
		return "Babası"
	case "ANNE":
		return "Annesi"
	case "COCUK":
		return "Çocuğu"
	case "ES":
		return "Eşi"
	case "KARDES":
		return "Kardeşi"
	case "YEGEN":
		return "Yeğeni"
	case "AMCA":
		return "Amcası"
	case "HALA":
		return "Halası"
	case "TEYZE":
		return "Teyzesi"
	case "DAYI":
		return "Dayısı"
	case "KUZEN":
		return "Kuzeni"
	case "TORUN":
		return "Torunu"
	case "DEDE":
		return "Dedesi"
	case "BABAANNE":
		return "Babaannesi"
	case "ANNEANNE":
		return "Anneannesi"
	case "NINE":
		return "Ninesi"
	case "BUYUK_DEDE":
		return "Büyük Dedesi"
	case "BUYUK_NINE":
		return "Büyük Ninesi"
	case "BUYUK_AMCA":
		return "Büyük Amcası"
	case "BUYUK_HALA":
		return "Büyük Halası"
	case "KUZEN_COCUGU":
		return "Kuzen Çocuğu"
	case "YEGEN_COCUGU":
		return "Yeğen Çocuğu"
	case "GELIN":
		return "Gelini"
	case "DAMAT":
		return "Damadı"
	case "KAYINVALIDE":
		return "Kayınvalidesi"
	case "KAYINPEDER":
		return "Kayınpederi"
	case "ENISTE":
		return "Eniştesi"
	case "BALDIZ":
		return "Baldızı"
	case "GORUMCE":
		return "Görümcesi"
	case "DUNUR":
		return "Dünürü"
	case "KAYINBIRADER":
		return "Kayınbiraderi"
	default:
		return tur
	}
}

func aileBaglariIdleriBul(ctx context.Context, kol *mongo.Collection, idler []string) []bson.M {
	var aramaIdler bson.A
	for _, idStr := range idler {
		if idStr == "" {
			continue
		}
		aramaIdler = append(aramaIdler,
			bson.M{"veri.kisi1": idStr},
			bson.M{"veri.kisi2": idStr},
		)
		if oid, err := primitive.ObjectIDFromHex(idStr); err == nil {
			aramaIdler = append(aramaIdler,
				bson.M{"veri.kisi1": oid},
				bson.M{"veri.kisi2": oid},
			)
		}
	}
	if len(aramaIdler) == 0 {
		return nil
	}

	cursor, err := kol.Find(ctx, bson.M{
		"vt":  "FikoYT.sistem.aileBaglari",
		"$or": aramaIdler,
	})
	if err != nil {
		return nil
	}
	defer cursor.Close(ctx)

	var sonuclar []bson.M
	for cursor.Next(ctx) {
		var bag bson.M
		cursor.Decode(&bag)
		sonuclar = append(sonuclar, bag)
	}
	return sonuclar
}

func kisiDetayGetir(ctx context.Context, kol *mongo.Collection, akrabaId string) bson.M {
	oid, err := primitive.ObjectIDFromHex(akrabaId)
	if err != nil {
		return nil
	}
	var akraba bson.M
	kol.FindOne(ctx, bson.M{"_id": oid, "vt": "FikoYT.sistem.soyAgaciKisiler"}).Decode(&akraba)
	if akraba == nil {
		kol.FindOne(ctx, bson.M{"vt": "FikoYT.sistem.soyAgaciKisiler", "veri.platformKullaniciId": akrabaId}).Decode(&akraba)
	}
	if akraba == nil {
		db := fiko_mongo.GetMongoDBClient().Database("fikoyt")
		var uye bson.M
		db.Collection("goUyeler").FindOne(ctx, bson.M{"_id": oid}).Decode(&uye)
		if uye != nil {
			akraba = bson.M{"veri": bson.M{"isim": uye["isim"], "soyIsim": uye["soyIsim"]}}
		}
	}
	return akraba
}

func kullaniciBaglamiOlustur(kullaniciId interface{}, fk *FikoYTCtx.FikoYTCtx) string {
	ctx := context.TODO()
	kol := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("hareketler")

	isim := fmt.Sprintf("%v", fk.AktifKullanici.Isim)
	soyIsim := fmt.Sprintf("%v", fk.AktifKullanici.SoyIsim)

	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("Aktif kullanıcı: %s %s\n", isim, soyIsim))

	var kullaniciIdHex string
	switch v := kullaniciId.(type) {
	case primitive.ObjectID:
		kullaniciIdHex = v.Hex()
	case string:
		kullaniciIdHex = v
	default:
		kullaniciIdHex = fmt.Sprintf("%v", kullaniciId)
	}

	var soyagaciKisi bson.M
	kol.FindOne(ctx, bson.M{
		"vt": "FikoYT.sistem.soyAgaciKisiler",
		"$or": bson.A{
			bson.M{"veri.platformKullaniciId": kullaniciIdHex},
			bson.M{"veri.platformKullaniciId": kullaniciId},
		},
	}).Decode(&soyagaciKisi)

	if soyagaciKisi == nil {
		return sb.String()
	}

	kisiId := soyagaciKisi["_id"]
	if veri, ok := soyagaciKisi["veri"].(bson.M); ok {
		if dogum, ok := veri["dogumTarihi"].(string); ok && dogum != "" {
			sb.WriteString(fmt.Sprintf("Doğum tarihi: %s\n", dogum))
		}
		if dogumYeri, ok := veri["dogumYeri"].(string); ok && dogumYeri != "" {
			sb.WriteString(fmt.Sprintf("Doğum yeri: %s\n", dogumYeri))
		}
		if meslek, ok := veri["meslek"].(string); ok && meslek != "" {
			sb.WriteString(fmt.Sprintf("Meslek: %s\n", meslek))
		}
		if medeni, ok := veri["medeniDurum"].(string); ok && medeni != "" {
			sb.WriteString(fmt.Sprintf("Medeni durum: %s\n", medeni))
		}
	}

	kisiIdStr := toHex(kisiId)
	if kisiIdStr == "" {
		return sb.String()
	}

	grafSonuc := kullaniciBaglamiGrafOlustur(kisiIdStr, kullaniciIdHex, ctx, kol)
	if grafSonuc != "" {
		sb.WriteString(grafSonuc)
	}

	return sb.String()
}

func konusmaSilHandler(c fiber.Ctx) error {
	fk, err := kullaniciAl(c)
	if err != nil {
		return err
	}

	idStr := c.Params("id")
	oid, err := primitive.ObjectIDFromHex(idStr)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"hata": "Geçersiz ID"})
	}

	kol := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("hareketler")
	kullaniciId := fk.AktifKullanici.IsleObjectId().Id

	result, err := kol.DeleteOne(context.TODO(), bson.M{
		"_id":               oid,
		"vt":                SohbetVT,
		"veri.kullanici_id": kullaniciId,
	})

	if err != nil || result.DeletedCount == 0 {
		return c.Status(404).JSON(fiber.Map{"hata": "Sohbet bulunamadı"})
	}

	return c.JSON(fiber.Map{"basarili": true})
}
