package utils

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"io"
	"net/http"
	"regexp"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"go.uber.org/zap"

	"golang.org/x/crypto/pbkdf2"
)

func SplitString(str string) []string {
	if len(str) == 0 {
		return []string{}
	}

	return regexp.MustCompile("[, ]+").Split(str, -1)
}

type HttpGetResponse struct {
	Body    []byte
	Headers http.Header
}

func HttpGet(client *http.Client, url string) (response HttpGetResponse, err error) {
	r, err := client.Get(url)
	if err != nil {
		return
	}

	defer func() {
		if err := r.Body.Close(); err != nil {
			log.Warn("Failed to close response body", zap.Error(err))
		}
	}()

	body, err := io.ReadAll(r.Body)
	if err != nil {
		return
	}

	response = HttpGetResponse{body, r.Header}

	if r.StatusCode >= 300 {
		err = errors.New(string(response.Body))
		return
	}

	err = nil
	return
}

func EncodePassword(password, salt string) (string, error) {
	newPasswd := pbkdf2.Key([]byte(password), []byte(salt), 10000, 50, sha256.New)
	return hex.EncodeToString(newPasswd), nil
}

func DecodeBasicAuthHeader(header string) (string, string, error) {
	var code string
	parts := strings.SplitN(header, " ", 2)
	if len(parts) == 2 && parts[0] == "Basic" {
		code = parts[1]
	}

	decoded, err := base64.StdEncoding.DecodeString(code)
	if err != nil {
		return "", "", err
	}

	userAndPass := strings.SplitN(string(decoded), ":", 2)
	if len(userAndPass) != 2 {
		return "", "", errors.New("invalid basic auth header")
	}

	return userAndPass[0], userAndPass[1], nil
}

type AskTUGClaims struct {
	AccessToken  string   `json:"access_token"`
	TokenType    []string `json:"token_type,omitempty"`
	ExpiresAt    int64    `json:"expires_in,omitempty"`
	IdToken      string   `json:"id_token,omitempty"`
	RefreshToken int64    `json:"refresh_token,omitempty"`
}

func AuthError(c *gin.Context, message string) {
	resp := map[string]string{"error": message}

	c.JSON(http.StatusUnauthorized, resp)
	c.Abort()
}

func ForbiddenError(c *gin.Context, message string) {
	resp := map[string]string{"error": message}

	c.JSON(http.StatusForbidden, resp)
	c.Abort()
}
