package uyar_sockets

import (
	"context"
	"log"
	FikoYTMap "server/sistem/fikoyt_map"
	fiko_mongo "server/sistem/fikoyt_mongo"
	fiko_socket_io "server/sistem/fikoyt_socket_io"
	"time"

	"github.com/gofiber/fiber/v3"
	"github.com/zishang520/socket.io/v2/socket"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo/options"
)

func Init(app *fiber.App) {
	baglantilariSil()
	fiko_socket_io.FikoSocketIo.On("connection", func(clients ...interface{}) {
		client := clients[0].(*socket.Socket)
		client.On("disconnect", func(args ...interface{}) {
			ctx := context.TODO()
			collection := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("uyar_dokum")

			query := bson.M{
				"db":            "FikoYT.uyar.devices.device",
				"info.socketId": 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["info"].(bson.M)["uuid"]

			/**/
			_, err := collection.UpdateMany(ctx, query, bson.M{
				"$pull": bson.M{"info.sockets": client.Id()},
				"$set": bson.M{
					"info.socketId": nil,
					"data":          nil,
				},
			})
			if err != nil {
				log.Printf("Failed to update device sockets: %v", err)
			}

			// Handle system connections
			query["db"] = "FikoYT.uyar.system.connections"
			_, _ = collection.UpdateMany(ctx, bson.M{
				"db":            "FikoYT.uyar.system.connections",
				"info.socketId": client.Id(),
			}, bson.M{"$set": bson.M{"eventSilinecek": true}})
			_, _ = collection.DeleteMany(ctx, bson.M{
				"db":            "FikoYT.uyar.system.connections",
				"info.socketId": client.Id(),
			})
			/**/
			// var cihaz bson.M
			// err8 := collection.FindOne(ctx, query).Decode(&cihaz)
			// if err8 != nil {
			// 	log.Printf("Error finding device: %v", err8)
			// 	return
			// }

			// // 'info' alanını doğru şekilde kontrol et ve 'uuid' değerini al
			// info, ok := cihaz["info"].(bson.M)
			// if !ok {
			// 	log.Println("Error: 'info' is not of type bson.M or is nil")
			// 	return
			// }

			// uuid, ok := info["uuid"].(string)
			// if !ok {
			// 	log.Println("Error: 'uuid' is not of type string or is nil")
			// 	return
			// }

			// 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{
						"db":        "FikoYT.uyar.devices.device",
						"info.uuid": cihaz["info"].(bson.M)["uuid"].(string),
					}).Decode(&cihazSon)
					if err4 != nil {
						log.Printf("Cihaz bulunamadı: %v", err4)
						return
					}

					// 'info' ve 'cihazUuid' alanlarına doğrudan erişim
					cihazSonUuid := cihazSon["info"].(bson.M)["uuid"].(string)

					socket.Emit("FikoYT.uyar.devices.card.update", bson.M{"cardUuid": "", "uuid": cihazSonUuid})
					socket.Emit(`FikoYT.uyar.devices.device.updated`, cihazSon)

					// socket.Emit("FikoYT.uyar.devices.device.updated", cihaz)
				})
			}
			/**/
			log.Println("FikoYT.uyar.devices.disconnected", args)
		})
		/**/
		client.On("FikoYT.uyar.devices.connected", func(args ...interface{}) {
			log.Println("FikoYT.uyar.devices.connected", client.Id())
			handleDeviceConnection(string(client.Id()), args[0].(map[string]interface{}))
		})

		/**/
		client.On("FikoYT.uyar.devices.card.update", func(args ...interface{}) {
			// log.Println("FikoYT.uyar.devices.card.update", args[0].(map[string]interface{})["cardUuid"])
			fikoytUyarCihazGuncelle(args[0].(map[string]interface{}), string(client.Id()))
		})
		/**/
		client.On("FikoYT.uyar.devices.info", func(args ...any) {
			// log.Println("FikoYT.uyar.devices.info", args[0].(string))
			deviceInfo, _ := fikoytUyarCihazBilgi(args[0].(string))
			ack := args[len(args)-1].(socket.Ack)
			ack([]any{deviceInfo}, nil)
		})
		/**/
		client.On("FikoYT.uyar.devices.list", func(args ...any) {
			// log.Println("FikoYT.uyar.devices.list", args[0].(string))
			deviceInfo := fikoytUyarCihazListe(args[0].(string))
			ack := args[len(args)-1].(socket.Ack)
			ack([]any{deviceInfo}, nil)
		})
		/**/
		client.On("FikoYT.uyar.devices.device.setCardReader", func(args ...interface{}) {
			// log.Println("FikoYT.uyar.devices.device.setCardReader", args[0].(map[string]interface{})["cardUuid"])
			deviceInfo, _ := fikoytUyarCihazKartOkuyucuAyarla(args[0].(map[string]interface{}))
			ack := args[len(args)-1].(socket.Ack)
			ack([]any{deviceInfo}, nil)
		})
		/**/
		client.On("FikoYT.uyar.nfc.kullanici", func(args ...interface{}) {
			log.Println("FikoYT.uyar.nfc.kullanici", args)
			var params map[string]any = args[1].(map[string]interface{})
			// log.Println("FikoYT.uyar.nfc.kullanici", params["nfc"])
			kInfo := fikoytUyarNfcUser(params["nfc"].(string))
			ack := args[len(args)-1].(socket.Ack)
			ack([]any{kInfo}, nil)
		})
	})
}

/**/
func fikoytUyarNfcUser(receivedData string) any {
	ctx := context.TODO()
	dbClient := fiko_mongo.GetMongoDBClient() // MongoDB bağlantısını döndüren varsayımsal fonksiyon
	collection := dbClient.Database("fikoyt").Collection("goUyeler")

	// Sorgu nesnesi oluştur
	query := fiber.Map{
		"nfc": fiber.Map{"$in": []string{receivedData}},
	}

	// Veritabanında cihazları ara
	var devices FikoYTMap.FikoYTMap
	cursor := collection.FindOne(ctx, query).Decode(&devices)
	if cursor != nil {
		print(cursor.Error())
		return nil
	}

	delete(devices, "parola")
	// Ack fonksiyonunu çağır ve cihaz listesini gönder
	return devices
}

/**/
func baglantilariSil() {
	collectionBaglantilar := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("uyar_dokum")

	// İlk sorgu: "FikoYT.uyar.system.connections" verisini sil
	sorgu := bson.M{
		"db": "FikoYT.uyar.system.connections",
	}

	_, err := collectionBaglantilar.DeleteMany(context.TODO(), sorgu)
	if err != nil {
		log.Fatalf("DeleteMany error: %v", err)
		return
	}

	// İkinci sorgu: "FikoYT.uyar.devices.device" verisini güncelle
	sorgu = bson.M{
		"db": "FikoYT.uyar.devices.device",
	}

	// UpdateMany işlemi, info.sockets'ı boş bir dizi olarak ayarlar
	update := bson.M{
		"$set": bson.M{
			"info.sockets": []interface{}{},
		},
	}

	collectionBaglantilar.UpdateMany(context.TODO(), sorgu, update, options.Update().SetUpsert(false))
}

/*
 */
func fikoytUyarCihazKartOkuyucuAyarla(receivedData map[string]interface{}) (bson.M, error) {
	ctx := context.TODO()
	dbClient := fiko_mongo.GetMongoDBClient() // MongoDB bağlantısını döndüren varsayımsal fonksiyon
	collection := dbClient.Database("fikoyt").Collection("uyar_dokum")
	// Sorgu nesnesi oluştur
	query := bson.M{
		"db":        "FikoYT.uyar.devices.device",
		"info.uuid": receivedData["deviceUuid"],
	}

	// Veritabanında güncelleme işlemi
	update := bson.M{
		"$set": bson.M{
			"info.settings.cardReaderUuid": receivedData["readerUuid"],
		},
		"$addToSet": bson.M{
			"info.linkedDevices": receivedData["readerUuid"],
		},
	}

	// UpdateMany işlemi
	_, err := collection.UpdateMany(ctx, query, update)
	if err != nil {
		log.Printf("Error updating device: %v", err)
		return nil, err
	}

	// Güncellenen cihazı al
	var updatedDevice bson.M
	err = collection.FindOne(ctx, query).Decode(&updatedDevice)
	if err != nil {
		log.Printf("Error finding device: %v", err)
		return nil, err
	}

	// Cihazı geri döndür
	return updatedDevice, nil
}

/*
 */
func fikoytUyarCihazListe(receivedData string) any {
	ctx := context.TODO()
	dbClient := fiko_mongo.GetMongoDBClient() // MongoDB bağlantısını döndüren varsayımsal fonksiyon
	collection := dbClient.Database("fikoyt").Collection("uyar_dokum")

	// Sorgu nesnesi oluştur
	query := bson.M{
		"db":        "FikoYT.uyar.devices.device",
		"info.type": receivedData, // Gelen 'type' değerini sorguda kullanıyoruz
	}

	// Veritabanında cihazları ara
	cursor, err := collection.Find(ctx, query)
	if err != nil {
		log.Printf("Error finding devices: %v", err)
		return nil
	}

	// Cihazları array olarak al
	var devices []bson.M
	if err := cursor.All(context.TODO(), &devices); err != nil {
		log.Printf("Error fetching devices: %v", err)
		return nil
	}

	// Ack fonksiyonunu çağır ve cihaz listesini gönder
	return devices
}

/**/
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("uyar_dokum")

	// Sorgu
	query := bson.M{
		"db":        "FikoYT.uyar.devices.device",
		"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 fikoytUyarCihazGuncelle(receivedData map[string]interface{}, clientID string) {
	// Logger ile gelen veriyi yazdır
	// log.Printf("FikoYT.uyar.devices.card.update \n %s\n", receivedData)

	// MongoDB koleksiyonu
	ctx := context.TODO()
	dbClient := fiko_mongo.GetMongoDBClient() // MongoDB bağlantısını sağlayan varsayımsal bir fonksiyon
	collection := dbClient.Database("fikoyt").Collection("uyar_dokum")

	// Sorgu
	query := bson.M{
		"db":        "FikoYT.uyar.devices.device",
		"info.uuid": receivedData["deviceUuid"],
	}

	// Güncelleme verisi
	update := bson.M{
		"$set": bson.M{
			"data": bson.M{
				"cardId": receivedData["cardUuid"],
				"time":   time.Now(),
			},
		},
	}

	// UpdateMany işlemi
	_, err := collection.UpdateMany(ctx, query, update, options.Update().SetUpsert(false))
	if err != nil {
		log.Printf("UpdateMany error: %v\n", err)
		return
	}

	// Bağlı cihazların soketlerini güncelle
	if uuid, ok := receivedData["deviceUuid"].(string); ok {
		wsLinkedDeviceSockets(uuid, func(socket *socket.RemoteSocket) {
			socket.Emit("FikoYT.uyar.devices.card.update", receivedData)
		})
	} else {
		log.Println("UUID is missing or not a string")
	}
}

/**/
func handleDeviceConnection(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.
	collection := dbClient.Database("fikoyt").Collection("uyar_dokum")

	// İlk sorgu ve kayıt
	query := bson.M{
		"db":            "FikoYT.uyar.system.connections",
		"info.socketId": clientID,
	}

	record := bson.M{
		"db":   "FikoYT.uyar.system.connections",
		"date": time.Now(),
		"info": bson.M{
			"socketId": clientID,
		},
	}

	// Gelen veriyi birleştir
	for key, value := range receivedData {
		record["info"].(bson.M)[key] = value
	}

	_, err := collection.UpdateMany(ctx, query, bson.M{"$set": record}, options.Update().SetUpsert(true))
	if err != nil {
		log.Printf("UpdateMany error: %v\n", err)
		return
	}

	// İkinci sorgu ve kayıt
	query1 := bson.M{
		"db":        "FikoYT.uyar.devices.device",
		"info.uuid": receivedData["deviceUuid"],
	}

	record1 := bson.M{
		"db":   "FikoYT.uyar.devices.device",
		"date": time.Now(),
		"info": bson.M{
			"uuid":     receivedData["deviceUuid"],
			"type":     receivedData["type"],
			"socketId": clientID,
		},
	}

	// Gelen veriyi birleştir
	for key, value := range receivedData {
		record1["info"].(bson.M)[key] = value
	}

	_, err = collection.UpdateMany(ctx, query1, bson.M{
		"$set": bson.M{
			"info.uuid":     record1["info"].(bson.M)["uuid"],
			"info.type":     record1["info"].(bson.M)["type"],
			"info.socketId": record1["info"].(bson.M)["socketId"],
		},
		"$addToSet": bson.M{"info.sockets": 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
	err = collection.FindOne(ctx, bson.M{
		"db":        "FikoYT.uyar.devices.device",
		"info.uuid": record1["info"].(bson.M)["uuid"],
	}).Decode(&device)
	if err != nil {
		log.Printf("FindOne error: %v\n", err)
		// return
	}

	// Bağlı cihazların soketlerini güncelle
	info, ok := device["info"].(bson.M)
	if !ok {
		log.Println("Device info is not in expected format")
		// return
	}

	uuid, ok := info["uuid"].(string)
	if !ok {
		log.Println("Device UUID is missing or not a string")
		// return
	}

	wsLinkedDeviceSockets(uuid, func(socket *socket.RemoteSocket) {
		socket.Emit("FikoYT.uyar.devices.device.updated", device)
	})
}

/**/
func wsLinkedDeviceSockets(deviceUuid string, cb func(socket *socket.RemoteSocket)) {

	ctx := context.TODO()
	collection := fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection("uyar_dokum")

	query := bson.M{
		"db":                 "FikoYT.uyar.devices.device",
		"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.
		fiko_socket_io.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)
	}

}
