package fiko_socket_io

import (
	"context"
	"log"
	"reflect"
	fiko_mongo "server/sistem/fikoyt_mongo"
	"time"

	"github.com/gofiber/fiber/v3"
	"github.com/gofiber/fiber/v3/middleware/adaptor"
	EIOLog "github.com/zishang520/engine.io/v2/log"
	"github.com/zishang520/engine.io/v2/types"
	"github.com/zishang520/socket.io/v2/socket"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo/options"
)

var FikoSocketIo *socket.Server

func Ayarla(app *fiber.App) {
	baglantilariSil()
	EIOLog.DEBUG = true
	socketioSettings := socket.DefaultServerOptions()
	socketioSettings.SetServeClient(true)
	// c.SetConnectionStateRecovery(&socket.ConnectionStateRecovery{})
	socketioSettings.SetAllowEIO3(true)
	socketioSettings.SetAllowUpgrades(true)
	socketioSettings.SetTransports(types.NewSet("polling", "websocket", "webtransport"))

	socketioSettings.SetPingInterval(2000 * time.Millisecond)
	socketioSettings.SetPingTimeout(2000 * time.Millisecond)
	socketioSettings.SetMaxHttpBufferSize(1000000)
	socketioSettings.SetConnectTimeout(3000 * time.Millisecond)
	socketioSettings.SetCors(&types.Cors{
		Origin:      "*",
		Credentials: true,
	})
	FikoSocketIo = socket.NewServer(nil, nil)

	FikoSocketIo.On("connection", func(clients ...interface{}) {
		client := clients[0].(*socket.Socket)

		// fmt.Println(clients...)

		ClientBaglandi(client)
	})

	//
	app.Get("/socket.io/", adaptor.HTTPHandler(FikoSocketIo.ServeHandler(socketioSettings)))
	app.Post("/socket.io/", adaptor.HTTPHandler(FikoSocketIo.ServeHandler(socketioSettings)))

	// FikoSocketIo.Use(func(socket *socket.Socket, next func(*socket.ExtendedError)) {
	// 	fmt.Println(socket.Request().Headers())
	// 	next(nil)
	// })
	// FikoSocketIo.FetchSockets()
}

// hasKey kontrol fonksiyonu
func hasKey(data interface{}, key string) bool {
	if reflect.TypeOf(data).Kind() == reflect.Map {
		return reflect.ValueOf(data).MapIndex(reflect.ValueOf(key)).IsValid()
	}
	return false
}

func ClientBaglandi(client *socket.Socket) {
	client.On("disconnect", func(args ...interface{}) {
		cihazKoptu(string(client.Id()), args[len(args)-2].(map[string]interface{}))
	})
	/**/
	client.On("FikoYT.sistem.io.cihaz.baglandi", func(args ...interface{}) {
		// log.Println("FikoYT.sistem.io.cihaz.baglandi", args[len(args)-2])
		cihazBaglandi(string(client.Id()), args[len(args)-2].(map[string]interface{}))
	})
	/**/
	client.On("FikoYT.sistem.io.cihaz.guncelle", func(args ...interface{}) {
		// log.Println("FikoYT.sistem.io.cihaz.guncelle", args[len(args)-2])
		cihazBaglandi(string(client.Id()), args[len(args)-2].(map[string]interface{}))
	})
}

/**/
func baglantilariSil() {
	collectionBaglantilar := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("hareketler")

	// İlk sorgu: "FikoYT.sistem.io.cihaz.baglantilar" verisini sil
	// sorgu := bson.M{
	// 	"vt": "FikoYT.sistem.io.cihaz.baglantilar",
	// }

	// _, err := collectionBaglantilar.DeleteMany(context.TODO(), sorgu)
	// if err != nil {
	// 	log.Fatalf("DeleteMany error: %v", err)
	// 	return
	// }

	// İkinci sorgu: "FikoYT.sistem.io.cihazlar.cihaz" verisini güncelle
	sorgu := bson.M{
		"vt": "FikoYT.sistem.io.cihazlar.cihaz",
	}

	// UpdateMany işlemi, info.sockets'ı boş bir dizi olarak ayarlar
	update := bson.M{
		"$set": bson.M{
			"veri.soketler": []interface{}{},
		},
	}

	collectionBaglantilar.UpdateMany(context.TODO(), sorgu, update, options.Update().SetUpsert(false))
}

/**/
func cihazKoptu(clientID string, receivedData map[string]interface{}) {
	ctx := context.TODO()
	collection := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("hareketler")

	query := bson.M{
		"vt": "FikoYT.sistem.io.cihazlar.cihaz",
		"veri.soketler": bson.M{
			"$in": []string{string(clientID)},
		},
	}

	/**/
	// cihadz,err1:= collection.Find(ctx, query, &options.FindOptions{})
	var cihaz bson.M
	err4 := collection.FindOne(ctx, query).Decode(&cihaz)
	if err4 != nil {
		// log.Printf("Cihaz bulunamadı: %v", err4)
		return
	}

	// 'info' ve 'cihazUuid' alanlarına doğrudan erişim
	cihazUuid := cihaz["veri"].(bson.M)["cihaz_kimlik"]

	/**/
	_, err := collection.UpdateMany(ctx, query, bson.M{
		"$pull": bson.M{"veri.soketler": clientID},
		"$set": bson.M{
			"data": nil,
		},
	})
	if err != nil {
		log.Printf("Failed to update device sockets: %v", err)
	}

	// Handle system connections
	query["vt"] = "FikoYT.sistem.io.cihaz.baglantilar"
	_, _ = collection.UpdateMany(ctx, bson.M{
		"vt": "FikoYT.sistem.io.cihaz.baglantilar",
		"veri.soketler": bson.M{
			"$in": []string{string(clientID)},
		},
	}, bson.M{"$set": bson.M{"eventSilinecek": true}})
	_, _ = collection.DeleteMany(ctx, bson.M{
		"vt": "FikoYT.sistem.io.cihaz.baglantilar",
		"veri.soketler": bson.M{
			"$in": []string{string(clientID)},
		},
	})
	/**/

	// Soket işlemini başlat
	if cihazUuid != nil {
		wsLinkedDeviceSockets(cihazUuid.(string), func(socket *socket.RemoteSocket) {
			var cihazSon bson.M
			err4 := collection.FindOne(ctx, bson.M{
				"vt":                "FikoYT.sistem.io.cihazlar.cihaz",
				"veri.cihaz_kimlik": cihaz["veri"].(bson.M)["cihaz_kimlik"].(string),
			}).Decode(&cihazSon)
			if err4 != nil {
				// log.Printf("Cihaz bulunamadı: %v", err4)
				return
			}

			// 'info' ve 'cihazUuid' alanlarına doğrudan erişim
			cihazSonUuid := cihazSon["veri"].(bson.M)["cihaz_kimlik"].(string)

			socket.Emit("FikoYT.uyar.devices.card.update", bson.M{"cardUuid": "", "uuid": cihazSonUuid})
			socket.Emit(`FikoYT.sistem.cihazlar.cihaz.updated`, cihazSon)

			// socket.Emit("FikoYT.sistem.cihazlar.cihaz.updated", cihaz)
		})
	}
	/**/
	// log.Println("FikoYT.uyar.devices.disconnected", args)
}

/**/
func cihazBaglandi(clientID string, receivedData map[string]interface{}) {
	// MongoDB bağlamı ve koleksiyonu
	ctx := context.TODO()
	dbClient := fiko_mongo.GetMongoDBClient() // MongoDB bağlantısını döndüren varsayımsal bir fonksiyon.
	collectionHareketler := dbClient.Database("fikoyt").Collection("hareketler")

	// // İlk sorgu ve kayıt
	// query := bson.M{
	// 	"vt":            "FikoYT.sistem.io.cihaz.baglantilar",
	// 	"info.socketId": clientID,
	// }

	// record := bson.M{
	// 	"vt":   "FikoYT.sistem.io.cihaz.baglantilar",
	// 	"date": time.Now(),
	// 	"info": bson.M{
	// 		"socketId": clientID,
	// 	},
	// }

	// // Gelen veriyi birleştir
	// for key, value := range receivedData {
	// 	record["info"].(bson.M)[key] = value
	// }

	// _, err := collectionHareketler.UpdateMany(ctx, query, bson.M{"$set": record}, options.Update().SetUpsert(true))
	// if err != nil {
	// 	log.Printf("UpdateMany error: %v\n", err)
	// 	return
	// }
	// log.Printf("Alınan Veri -------->%s", receivedData["FikoYT"].(map[string]interface{})["kullanici_kimlik"])
	// İkinci sorgu ve kayıt
	query1 := bson.M{
		"vt":                "FikoYT.sistem.io.cihazlar.cihaz",
		"veri.cihaz_kimlik": receivedData["FikoYT"].(map[string]interface{})["cihaz_kimlik"],
	}

	record1 := bson.M{
		"vt":    "FikoYT.sistem.io.cihazlar.cihaz",
		"tarih": time.Now(),
		"veri": bson.M{
			"cihaz_kimlik":     receivedData["FikoYT"].(map[string]interface{})["cihaz_kimlik"],
			"cihaz_tipi":       receivedData["FikoYT"].(map[string]interface{})["cihaz_tipi"],
			"cihaz_bilgi":      receivedData["FikoYT"].(map[string]interface{})["cihaz_bilgi"],
			"kullanici_kimlik": receivedData["FikoYT"].(map[string]interface{})["kullanici_kimlik"],
		},
	}

	// Gelen veriyi birleştir
	// for key, value := range receivedData["FikoYT"].(map[string]interface{}) {
	// 	record1["veri"].(bson.M)[key] = value
	// }

	_, err := collectionHareketler.UpdateMany(ctx, query1, bson.M{
		"$set": bson.M{
			"veri.cihaz_kimlik":     record1["veri"].(bson.M)["cihaz_kimlik"],
			"veri.cihaz_tipi":       record1["veri"].(bson.M)["cihaz_tipi"],
			"veri.cihaz_bilgi":      record1["veri"].(bson.M)["cihaz_bilgi"],
			"veri.kullanici_kimlik": record1["veri"].(bson.M)["kullanici_kimlik"],
		},
		"$addToSet": bson.M{"veri.soketler": clientID},
	}, options.Update().SetUpsert(true))
	if err != nil {
		log.Printf("UpdateMany (devices) error: %v\n", err)
		// return
	}

	// Cihazı bul ve soket güncellemesini tetikle
	var device bson.M
	err1 := collectionHareketler.FindOne(ctx, bson.M{
		"vt":                "FikoYT.sistem.io.cihazlar.cihaz",
		"veri.cihaz_kimlik": record1["veri"].(bson.M)["cihaz_kimlik"],
	}).Decode(&device)
	if err1 != nil {
		log.Printf("FindOne error: %v\n", err1)
		// return
	}

	// Bağlı cihazların soketlerini güncelle
	info, ok := device["veri"].(bson.M)
	if !ok {
		log.Println("Device info is not in expected format")
		// return
	}

	uuid, ok := info["cihaz_kimlik"].(string)
	if !ok {
		log.Println("Device UUID is missing or not a string")
		// return
	}

	wsLinkedDeviceSockets(uuid, func(socket *socket.RemoteSocket) {
		socket.Emit("FikoYT.sistem.cihazlar.cihaz.guncellendi", device)
	})
}

/**/
func fikoytUyarCihazBilgi(receivedData string) (map[string]interface{}, error) {
	// MongoDB bağlamı
	ctx := context.TODO()
	dbClient := fiko_mongo.GetMongoDBClient() // MongoDB bağlantısını döndüren varsayımsal fonksiyon
	collection := dbClient.Database("fikoyt").Collection("hareketler")

	// Sorgu
	query := bson.M{
		"vt":        "FikoYT.sistem.io.cihazlar.cihaz",
		"info.uuid": receivedData,
	}

	// Cihaz bilgilerini sorgulama
	var deviceInfo map[string]interface{}
	err := collection.FindOne(ctx, query).Decode(&deviceInfo)
	if err != nil {
		return nil, err
	}

	return deviceInfo, nil
}

/**/
func wsLinkedDeviceSockets(deviceUuid string, cb func(socket *socket.RemoteSocket)) {

	ctx := context.TODO()
	collection := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("hareketler")

	query := bson.M{
		"vt":                 "FikoYT.sistem.io.cihazlar.cihaz",
		"info.linkedDevices": bson.M{"$in": bson.A{deviceUuid}},
	}
	cursor, err := collection.Find(ctx, query)
	if err != nil {
		log.Printf("MongoDB sorgu hatası: %v\n", err)
		return
	}
	defer cursor.Close(ctx)

	// log.Println(cursor)

	// Sonuçları dolaş
	for cursor.Next(ctx) {
		var result bson.M
		if err := cursor.Decode(&result); err != nil {
			// log.Printf("Sonuç çözümleme hatası: %v\n", err)
			continue
		}

		// info.socketId'yi kontrol et
		info, ok := result["info"].(bson.M)
		if !ok {
			// log.Println("Info alanı uygun formatta değil.")
			continue
		}

		socketId, ok := info["socketId"].(string)
		if !ok {
			// log.Println("socketId uygun formatta değil.")
			continue
		}

		// Tüm socketleri getir (örneğin bir cache ya da bağlı soket havuzundan)
		// allSockets, _ := fiko_socket_io.FikoSocketIo.Sockets().AllSockets() // Bu, mevcut tüm socket'leri döndüren varsayımsal bir fonksiyondur.
		FikoSocketIo.FetchSockets()(func(sockets []*socket.RemoteSocket, _ error) {
			for _, v := range sockets {
				if v.Id() == socket.SocketId(socketId) {
					if cb != nil {
						cb(v)
					}
				}
			}
		})

	}

	if err := cursor.Err(); err != nil {
		log.Printf("Cursor hatası: %v\n", err)
	}

}

/***/
