package main

import (
	"context"
	"log"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/logger"
	"github.com/gofiber/fiber/v2/middleware/recover"
	"github.com/gofiber/fiber/v2/middleware/session"
	"github.com/gofiber/websocket/v2"
	"github.com/gorilla/securecookie"
	"github.com/mongodb/mongo-go-driver/mongo"
	"github.com/mongodb/mongo-go-driver/mongo/gridfs"
	"go.mongodb.org/mongo-driver/mongo/options"
)

var (
	sslCertFile    = "./certs/server.crt"
	sslKeyFile     = "./certs/server.key"
	mongoURI       = "mongodb://localhost:27017"
	databaseName   = "exampledb"
	sessionStore   = session.New()
	secureCookie   = securecookie.New([]byte("very-secret-key"), []byte("a-lot-secret-key"))
	mongoClient    *mongo.Client
	gridFSBucket   *gridfs.Bucket
	socketSessions = make(map[string]*websocket.Conn)
)

func main() {
	// MongoDB bağlantısını başlat
	initMongo()

	// Fiber uygulamasını oluştur
	app := fiber.New(fiber.Config{
		Prefork:      false,
		IdleTimeout:  5 * time.Second,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
	})

	// Middleware
	app.Use(logger.New())
	app.Use(recover.New())

	// Rotaları kur
	setupRoutes(app)

	// SSL ile başlat
	log.Println("Sunucu başlatılıyor: https://localhost:3000")
	if err := app.ListenTLS(":3000", sslCertFile, sslKeyFile); err != nil {
		log.Fatalf("SSL başlatma hatası: %v", err)
	}
}

func initMongo() {
	var err error
	mongoClient, err = mongo.Connect(context.TODO(), options.Client().ApplyURI(mongoURI))
	if err != nil {
		log.Fatalf("MongoDB bağlantı hatası: %v", err)
	}
	gridFSBucket, err = gridfs.NewBucket(mongoClient.Database(databaseName))
	if err != nil {
		log.Fatalf("GridFS bağlantı hatası: %v", err)
	}
	log.Println("MongoDB bağlantısı başarılı.")
}

func setupRoutes(app *fiber.App) {
	api := app.Group("/api")

	// Sağlık kontrolü
	api.Get("/health", func(c *fiber.Ctx) error {
		return c.JSON(fiber.Map{"status": "ok"})
	})

	// Dosya yükleme
	api.Post("/upload", handleFileUpload)

	// Socket.IO WebSocket
	app.Use("/ws", websocket.New(handleWebSocket))

	// Session örneği
	api.Get("/session", handleSession)
}

func handleFileUpload(c *fiber.Ctx) error {
	file, err := c.FormFile("file")
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Dosya yüklenemedi"})
	}

	// Dosyayı aç
	fileData, err := file.Open()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Dosya açılamadı"})
	}
	defer fileData.Close()

	// Dosyayı GridFS'e yükle
	uploadStream, err := gridFSBucket.OpenUploadStream(file.Filename)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "GridFS yükleme hatası"})
	}
	defer uploadStream.Close()

	if _, err := uploadStream.Write(fileData); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Yazma hatası"})
	}

	return c.JSON(fiber.Map{"message": "Dosya başarıyla yüklendi"})
}

func handleWebSocket(c *websocket.Conn) {
	defer c.Close()

	clientID := c.Query("id")
	socketSessions[clientID] = c
	defer delete(socketSessions, clientID)

	for {
		mt, message, err := c.ReadMessage()
		if err != nil {
			log.Printf("WebSocket hata: %v", err)
			break
		}

		log.Printf("Mesaj alındı: %s", string(message))

		if err := c.WriteMessage(mt, message); err != nil {
			log.Printf("Mesaj gönderim hatası: %v", err)
			break
		}
	}
}

func handleSession(c *fiber.Ctx) error {
	sess, err := sessionStore.Get(c)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Session alınamadı"})
	}

	// Örnek session işlemleri
	sess.Set("user_id", "12345")
	if err := sess.Save(); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Session kaydedilemedi"})
	}

	return c.JSON(fiber.Map{"session": sess.Get("user_id")})
}
