package auth

import (
	"errors"
	"fmt"

	"github.com/sujit-baniya/fiber-boilerplate/config"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/session/v2"
	"github.com/sujit-baniya/fiber-boilerplate/app"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
)

func User(c *fiber.Ctx) (*models.Users, error) {
	store := app.Http.Session.Get(c)
	userID := store.Get("user_id")

	// Si no hay sesión activa, intentar reconstruirla desde el JWT
	if userID == nil {
		id, err := app.Http.Token.ParseToken(c, app.Http.Token.AppJwtSecret)
		if err != nil || id == 0 {
			return nil, errors.New("User Not Logged In")
		}
		store.Set("user_id", id)
		store.Save()
		userID = id
	}

	user, err := models.FindUserByID(userID)
	if err != nil {
		return nil, err
	}
	return user, nil
}

func IsAdmin(c *fiber.Ctx) bool {
	user, err := User(c)
	if err != nil {
		return false
	}
	return user.IsAdmin
}

func UserID(c *fiber.Ctx) uint {
	store := app.Http.Session.Get(c)
	return store.Get("user_id").(uint)
}

func IsLoggedIn(c *fiber.Ctx) bool {
	store := app.Http.Session.Get(c)
	userID := store.Get("user_id")

	// Si no hay sesión, intentar reconstruirla desde el JWT
	if userID == nil {
		id, err := app.Http.Token.ParseToken(c, app.Http.Token.AppJwtSecret)
		if err != nil || id == 0 {
			DeleteSession(store)
			c.ClearCookie()
			return false
		}
		store.Set("user_id", id)
		store.Save()
		userID = id
	}

	token := c.Cookies("Verify-Rest-Token")
	if token == "" {
		tokenHash := store.Get("user_token")
		if tokenHash != nil {
			c.Cookie(&fiber.Cookie{
				Name:     "Verify-Rest-Token",
				Value:    fmt.Sprintf("%s", tokenHash),
				Secure:   false,
				HTTPOnly: true,
			})
		}
	}

	return true
}

func Login(c *fiber.Ctx, userID uint, secret string) (*config.Token, error) {
	// Obtener o crear una nueva sesión
	store := app.Http.Session.Get(c) // get/create new session

	// Regenerar la sesión para evitar compartirla
	if err := store.Regenerate(); err != nil {
		return nil, fmt.Errorf("failed to regenerate session: %v", err)
	}

	// Guardar el user_id en la sesión
	store.Set("user_id", userID)
	c.Locals("user_id", userID) // Establecer el user_id en el contexto

	// Crear el token JWT
	token, err := app.Http.Token.CreateToken(c, userID, secret)
	if err != nil {
		return nil, err
	}

	// Guardar el token en la sesión
	store.Set("user_token", token.Hash)
	store.Set("token_expiry", token.Expire)
	store.Save() // Guardar la sesión de manera persistente

	return token, nil
}

func Logout(c *fiber.Ctx) error {
	store := app.Http.Session.Get(c)
	DeleteSession(store)
	c.ClearCookie()
	return nil
}

func AuthCookie(c *fiber.Ctx) error {
	IsLoggedIn(c)
	return c.Next()
}

func DeleteSession(store *session.Store) {
	store.Delete("user_id")
	store.Delete("user_token")
	err := store.Save()
	if err != nil {
		panic(err)
	}
}
