package usecase

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"time"

	"backend/pkg/permission/domain/entity"

	"github.com/golang-jwt/jwt/v4"
)

var accessTokenGenerator = createAccessToken
var refreshTokenGenerator = createRefreshToken

// createAccessToken 生成訪問令牌（Access Token）
func createAccessToken(token entity.Token, data any, secretKey string) (string, error) {
	claims := entity.Claims{
		Data: data,
		RegisteredClaims: jwt.RegisteredClaims{
			ID:        token.ID,
			ExpiresAt: jwt.NewNumericDate(time.Unix(int64(token.ExpiresIn), 0)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "permission",
		},
	}

	accessToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).
		SignedString([]byte(secretKey))
	if err != nil {
		return "", err
	}

	return accessToken, nil
}

// createRefreshToken 基於訪問令牌生成刷新令牌（Refresh Token）
func createRefreshToken(accessToken string) string {
	hash := sha256.New()
	_, _ = hash.Write([]byte(accessToken))

	return hex.EncodeToString(hash.Sum(nil))
}

func parseToken(accessToken string, secret string, validate bool) (jwt.MapClaims, error) {
	// 跳過驗證的解析
	var token *jwt.Token
	var err error

	if validate {
		token, err = jwt.Parse(accessToken, func(token *jwt.Token) (interface{}, error) {
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("token unexpected signing method: %v", token.Header["alg"])
			}

			return []byte(secret), nil
		})
		if err != nil {
			return jwt.MapClaims{}, err
		}
	} else {
		parser := jwt.NewParser(jwt.WithoutClaimsValidation())
		token, err = parser.Parse(accessToken, func(_ *jwt.Token) (any, error) {
			return []byte(secret), nil
		})
		if err != nil {
			return jwt.MapClaims{}, err
		}
	}

	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok && token.Valid {
		return jwt.MapClaims{}, fmt.Errorf("token valid error")
	}

	return claims, nil
}

func ParseClaims(accessToken string, secret string, validate bool) (TokenClaims, error) {
	claimMap, err := parseToken(accessToken, secret, validate)
	if err != nil {
		return TokenClaims{}, err
	}

	claimsData, ok := claimMap["data"].(map[string]any)
	if ok {
		return convertMap(claimsData), nil
	}

	return TokenClaims{}, fmt.Errorf("get data from claim map error")
}

func convertMap(input map[string]interface{}) map[string]string {
	output := make(map[string]string)
	for key, value := range input {
		switch v := value.(type) {
		case string:
			output[key] = v
		case fmt.Stringer:
			output[key] = v.String()
		default:
			output[key] = fmt.Sprintf("%v", value)
		}
	}

	return output
}
