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{}) {
		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(client.Id())},
			},
		}

		/**/
		// 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": client.Id()},
			"$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(client.Id())},
			},
		}, 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(client.Id())},
			},
		})
		/**/

		// 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)
	})
	/**/
	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{}))
	})
}

/**/
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 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)
	}

}
