package utils

import (
	"crypto/md5"
	"crypto/sha256"
	"encoding/hex"
	"time"
	"xboard-go/internal/config"

	"github.com/golang-jwt/jwt/v5"
	"golang.org/x/crypto/bcrypt"
)

type Claims struct {
	UserID  int  `json:"user_id"`
	IsAdmin bool `json:"is_admin"`
	jwt.RegisteredClaims
}

func GenerateToken(userID int, isAdmin bool) (string, error) {
	claims := Claims{
		UserID:  userID,
		IsAdmin: isAdmin,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * 7 * time.Hour)), // 7 days
			IssuedAt:  jwt.NewNumericDate(time.Now()),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(config.AppConfig.JWTSecret))
}

func VerifyToken(tokenString string) (*Claims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(config.AppConfig.JWTSecret), nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*Claims); ok && token.Valid {
		return claims, nil
	}

	return nil, jwt.ErrSignatureInvalid
}

func HashPassword(password string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	return string(bytes), err
}

func CheckPassword(password, hash string, algo, salt *string) bool {
	if algo != nil {
		switch *algo {
		case "md5":
			sum := md5.Sum([]byte(password))
			return hex.EncodeToString(sum[:]) == hash
		case "sha256":
			sum := sha256.Sum256([]byte(password))
			return hex.EncodeToString(sum[:]) == hash
		case "md5salt":
			sum := md5.Sum([]byte(password + stringValue(salt)))
			return hex.EncodeToString(sum[:]) == hash
		case "sha256salt":
			sum := sha256.Sum256([]byte(password + stringValue(salt)))
			return hex.EncodeToString(sum[:]) == hash
		}
	}

	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}

func stringValue(value *string) string {
	if value == nil {
		return ""
	}
	return *value
}
