package FikoYTCtx

import (
	"context"
	"fmt"
	"server/sistem/FikoYTDosya"
	FikoYTCevap "server/sistem/fikoyt_ctx/fikoyt_cevap"
	FikoYTCevapCls "server/sistem/fikoyt_ctx/fikoyt_cevap_cls"
	FikoYTIstek "server/sistem/fikoyt_ctx/fikoyt_istek"
	FikoYTKullanici "server/sistem/fikoyt_ctx/fikoyt_kullanici"
	"server/sistem/fikoyt_json"
	fiko_mongo "server/sistem/fikoyt_mongo"
	"server/sistem/socket_ortak"

	"github.com/gofiber/fiber/v3"
	"github.com/gofiber/fiber/v3/middleware/requestid"
	"github.com/gofiber/fiber/v3/middleware/session"
	"github.com/zishang520/socket.io/v2/socket"
	"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"
)

/**/

/**/
type FikoYTCtx struct {
	Istek            *FikoYTIstek.FikoYTIstek
	Cevap            *FikoYTCevapCls.FikoYTCevap
	Sezon            *session.Session
	Koleksiyon       func(koleksiyonIsmi string) *mongo.Collection
	EkleYadaGuncelle func(kol *mongo.Collection, veri interface{}) ([]*mongo.UpdateResult, error)
	IoClient         func() *socket.RemoteSocket
	IoServer         *socket.Server
	AktifKullanici   *FikoYTKullanici.FikoYTKullanici
}

func Ayarla(app *fiber.App) {
	// app.NewCtxFunc(func(app1 *fiber.App) fiber.CustomCtx {
	// 	return &Ctx{
	// 		Ctx: fiber.NewDefaultCtx(app1),
	// 	}
	// })

	app.Use(fikoyt_json.AraIsleyici())
	app.Use(FikoYTDosya.AraIsleyici())
	FikoYTCevap.Ayarla(app)
	app.Use(requestid.New())

	fiko_mongo.Init(app)
	app.Use(araIsleyici())

}

func araIsleyici() fiber.Handler {
	return func(c fiber.Ctx) error {
		sezon, _ := c.Locals("session").(*session.Session)
		/**/
		c.Locals("Cevap", &FikoYTCevapCls.FikoYTCevap{})
		cevap := c.Locals("Cevap").(*FikoYTCevapCls.FikoYTCevap)

		fkCtx := &FikoYTCtx{
			Istek:            FikoYTIstek.Yeni(c),
			Cevap:            cevap,
			Sezon:            sezon,
			Koleksiyon:       FikoYTCollection,
			EkleYadaGuncelle: EkleYadaGuncelle,
			IoClient:         alClientSocket(c),
			IoServer:         socket_ortak.FikoSocketIo,
			AktifKullanici:   alAktifKullanici(c),
		}
		/**/
		c.Locals("FikoYTCtx", fkCtx)
		return c.Next() // Sonraki handler'ı çağır
	}
}

func FikoYTCollection(koleksiyonIsmi string) *mongo.Collection {
	return fiko_mongo.GetMongoDBClient().Database("fikoyt").Collection(koleksiyonIsmi)
}

func EkleYadaGuncelle(kol *mongo.Collection, veri interface{}) ([]*mongo.UpdateResult, error) {
	var results []*mongo.UpdateResult

	switch v := veri.(type) {
	case map[string]interface{}:
		// Tekil obje
		res, err := updateOrInsertOne(kol, v)
		if err != nil {
			return nil, err
		}
		results = append(results, res)

	case []interface{}:

		// Liste, her elemanı map olmalı
		for i, item := range v {
			m, ok := item.(map[string]interface{})

			if !ok {
				return nil, fmt.Errorf("liste içindeki %d. eleman map değil: %T", i, item)
			}

			res, err := updateOrInsertOne(kol, m)

			if err != nil {
				fmt.Println(err)
				return nil, fmt.Errorf("%d. kayıt işlenirken hata: %v", i, err)
			}
			results = append(results, res)
		}

	default:

		return nil, fmt.Errorf("desteklenmeyen veri tipi: %T", veri)
	}
	fmt.Println(results)
	return results, nil
}

func updateOrInsertOne(kol *mongo.Collection, veri map[string]interface{}) (*mongo.UpdateResult, error) {
	var filter bson.M

	if id, ok := veri["_id"]; ok {
		filter = bson.M{"_id": id}
	} else {
		filter = bson.M{"_id": primitive.NewObjectID()}
	}

	update := bson.M{"$set": veri}

	return kol.UpdateOne(context.TODO(), filter, update, options.Update().SetUpsert(true))
}

func alClientSocket(c fiber.Ctx) func() *socket.RemoteSocket {
	return func() *socket.RemoteSocket {
		var rslt *socket.RemoteSocket = nil
		fsidHeader := c.GetReqHeaders()["Fsid"]
		if len(fsidHeader) > 0 {
			fsid := fsidHeader[0]
			socket_ortak.FikoSocketIo.FetchSockets()(func(sockets []*socket.RemoteSocket, _ error) {
				for _, v := range sockets {
					if v.Id() == socket.SocketId(fsid) {
						rslt = v
						break
					}
				}
			})
		}

		return rslt
	}
}

func alAktifKullanici(c fiber.Ctx) *FikoYTKullanici.FikoYTKullanici {
	// return func() *FikoYTKullanici.FikoYTKullanici {

	var rslt *FikoYTKullanici.FikoYTKullanici = nil
	sezon, _ := c.Locals("session").(*session.Session)
	objectIDStr := sezon.Get("aktifKullaniciID")
	if objectIDStr == "" {
		objectIDStr = nil
	}
	// fmt.Println(objectIDStr)

	if objectIDStr != nil {
		rslt = FikoYTKullanici.Yeni(c, objectIDStr.(string))
		// fmt.Println(aktifKullanici)
	}

	return rslt
	// }
}
