package utils

import (
	"PaymentCenter/app/constants/common"
	"PaymentCenter/config"
	"context"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/bytedance/sonic"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v4"
	"github.com/pkg/errors"
	"github.com/qit-team/snow-core/redis"
	"github.com/tjfoc/gmsm/sm2"
	"github.com/tjfoc/gmsm/x509"
	mrand "math/rand"
	"net"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"runtime"
	"strings"
	"time"
	"unicode"
)

const (
	CODE62  = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	codeLen = 20
)

func GetHostIp() string {
	conn, err := net.Dial("udp", "8.8.8.8:53")
	if err != nil {
		fmt.Println("get current host ip err: ", err)
		return ""
	}
	addr := conn.LocalAddr().(*net.UDPAddr)
	ip := strings.Split(addr.String(), ":")[0]
	return ip
}
func Log(c *gin.Context, name string, msg ...interface{}) {
	_, file, line, _ := runtime.Caller(1)
	var datetime = time.Now().Format("2006-01-02 15:04:05")
	fmt.Println(name, msg, file, line, datetime)
}

func GetRealKey(key string) string {
	return config.GetConf().ServiceName + ":" + key
}

// MD5加密
func SToMd5(data string) string {
	r := md5.Sum([]byte(data))
	return hex.EncodeToString(r[:])

}

/**
 * 编码 整数 为 base62 字符串
 */
func Encode(number int64) string {
	if number == 0 {
		return "0"
	}
	result := make([]byte, 0)
	for number > 0 {
		round := number / codeLen
		remain := number % codeLen
		result = append(result, CODE62[remain])
		number = round
	}
	return string(result)
}

// 生成用户touken
func GeneratorToken(playerName string, playerId int) string {
	//去生成一个token返回给客户端
	m5 := SToMd5(playerName + time.Now().String())
	var pid = int64(playerId)
	bsCode := Encode(pid)
	tk := m5 + bsCode
	//将token放入redis
	_, err := redis.GetRedis(redis.SingletonMain).SetEX(context.Background(), GetRealKey(common.TOKEN_PRE+tk), playerId, time.Duration(3600)*time.Second).Result()
	if err != nil {
		Log(nil, "token", err)
	}
	return tk
}

// ExistFile 检查给定的文件是否存在
func ExistFile(dirPath string) (exist bool, err error) {
	// 使用filepath.Abs获取绝对路径,确保我们处理的是实际存在的路径
	absPath, err := filepath.Abs(dirPath)
	if err != nil {
		return exist, fmt.Errorf("failed to get absolute path: %v", err)
	}

	// 使用os.Stat检查路径是否存在
	_, err = os.Stat(absPath)
	if errors.Is(err, os.ErrNotExist) {
		return exist, nil
	}

	return true, err
}

// StrToTimeShanghai 字符串转时间
func StrToTimeShanghai(strTime string) (t time.Time, err error) {
	location, _ := time.LoadLocation("Asia/Shanghai")
	return time.ParseInLocation(time.DateTime, strTime, location)
}

func GenIncrementId(tableName string) (int, error) {
	var id, err = redis.GetRedis().Incr(context.Background(), GetRealKey(tableName)).Result()

	return int(id), err
}

// CheckOrCreateYmdDirectory 创建ymd目录
func CheckOrCreateYmdDirectory(dirPath string) (SavePath string, err error) {
	// 使用filepath.Abs获取绝对路径,确保我们处理的是实际存在的路径
	now := time.Now()
	year := now.Year()
	month := now.Month()
	day := now.Day()
	savePath := filepath.Join("uploads", dirPath, fmt.Sprintf("%d", year), fmt.Sprintf("%d", month), fmt.Sprintf("%d", day))
	absPath, err := filepath.Abs(savePath)
	if err != nil {
		return SavePath, fmt.Errorf("failed to get absolute path: %v", err)
	}
	// 使用os.Stat检查路径是否存在
	info, err := os.Stat(absPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return SavePath, fmt.Errorf("error checking directory: %v", err)
		}

		// 目录不存在,尝试创建
		err = os.MkdirAll(absPath, 0755) // 0755是默认权限,可以按需调整
		if err != nil {
			return SavePath, fmt.Errorf("failed to create directory: %v", err)
		}
	} else if !info.IsDir() {
		return SavePath, fmt.Errorf("%s exists but it's not a directory", absPath)
	}
	SavePath = absPath
	return SavePath, nil
}

// CheckOrCreateDirectory 检查给定的目录是否存在,如果不存在则创建它
func CheckOrCreateDirectory(dirPath string) error {
	// 使用filepath.Abs获取绝对路径,确保我们处理的是实际存在的路径
	absPath, err := filepath.Abs(dirPath)
	if err != nil {
		return fmt.Errorf("failed to get absolute path: %v", err)
	}

	// 使用os.Stat检查路径是否存在
	info, err := os.Stat(absPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return fmt.Errorf("error checking directory: %v", err)
		}

		// 目录不存在,尝试创建
		err = os.MkdirAll(absPath, 0755) // 0755是默认权限,可以按需调整
		if err != nil {
			return fmt.Errorf("failed to create directory: %v", err)
		}
	} else if !info.IsDir() {
		return fmt.Errorf("%s exists but it's not a directory", absPath)
	}

	return nil
}

func ToSnakeCase(s string) string {
	ch, en := splitChineseEnglish(s)
	fmt.Println(ch, en)
	re := regexp.MustCompile("([a-z0-9])([A-Z])")
	snake := re.ReplaceAllString(en, "${1}_${2}")
	return strings.ToLower(snake) + ch
}

func splitChineseEnglish(input string) (chinese string, english string) {
	var index = findChineseStartIndex(input)

	return input[index:], input[0:index]
}

func findChineseStartIndex(input string) int {
	runes := []rune(input)
	for i, r := range runes {
		if unicode.Is(unicode.Han, r) {
			return i
		}
	}
	return -1 // 如果没有找到中文字符,返回-1
}

func DownloadFileFromOss(url, savePath string) error {
	_, bucket, err := AliOssClient()
	if err != nil {
		return err
	}
	err = bucket.GetObjectToFileWithURL(url, savePath)
	return err
}

func EntityCopy(dst, src interface{}) {
	dstValue := reflect.ValueOf(dst).Elem()
	srcValue := reflect.ValueOf(src).Elem()

	for i := 0; i < srcValue.NumField(); i++ {
		srcField := srcValue.Field(i)

		srcName := srcValue.Type().Field(i).Name
		dstFieldByName := dstValue.FieldByName(srcName)

		if dstFieldByName.IsValid() {
			switch dstFieldByName.Kind() {
			case reflect.Ptr:
				switch srcField.Kind() {
				case reflect.Ptr:
					if srcField.IsNil() {
						dstFieldByName.Set(reflect.New(dstFieldByName.Type().Elem()))
					} else {
						dstFieldByName.Set(srcField)
					}
				default:
					dstFieldByName.Set(srcField.Addr())
				}
			default:
				switch srcField.Kind() {
				case reflect.Ptr:
					if srcField.IsNil() {
						dstFieldByName.Set(reflect.Zero(dstFieldByName.Type()))
					} else {
						dstFieldByName.Set(srcField.Elem())
					}
				default:
					if srcField.Type().Name() == "Time" {
						if (srcField.Interface().(time.Time).Unix()) < 1 {
							dstFieldByName.Set(reflect.ValueOf(""))
						} else {
							dstFieldByName.Set(reflect.ValueOf(srcField.Interface().(time.Time).Format("2006-01-02 15:04:05")))
						}

					} else {
						dstFieldByName.Set(srcField)
					}

				}
			}
		}
	}
}

// AliOssClient 返回Oss客户链接
func AliOssClient() (client *oss.Client, Bucket *oss.Bucket, err error) {
	/*
	   oss 的相关配置信息
	*/
	bucketName := config.GetConf().AliOss.BucKet
	endpoint := config.GetConf().AliOss.EndPoint
	accessKeyId := config.GetConf().AliOss.AccessKey
	accessKeySecret := config.GetConf().AliOss.AccessKeySecret
	//domain := config.GetConf().AliOss.Domain
	//Dir := config.GetConf().AliOss.Dir

	//创建OSSClient实例
	client, err = oss.New(endpoint, accessKeyId, accessKeySecret)
	if err != nil {
		return nil, nil, err
	}

	// 获取存储空间
	Bucket, err = client.Bucket(bucketName)
	if err != nil {
		return nil, nil, err
	}
	return client, Bucket, nil
}
func GetSHA256HashCode(message []byte) string {
	//方法一:
	//创建一个基于SHA256算法的hash.Hash接口的对象
	hash := sha256.New()
	//输入数据
	hash.Write(message)
	//计算哈希值
	bytes := hash.Sum(nil)
	//将字符串编码为16进制格式,返回字符串
	hashCode := hex.EncodeToString(bytes)
	//返回哈希值
	return hashCode

	//方法二:
	//bytes2:=sha256.Sum256(message)//计算哈希值,返回一个长度为32的数组
	//hashcode2:=hex.EncodeToString(bytes2[:])//将数组转换成切片,转换成16进制,返回字符串
	//return hashcode2
}
func GetSHA512HashCode(message []byte) string {

	hash := sha512.New()
	hash.Write(message)
	bytes := hash.Sum(nil)
	hashCode := hex.EncodeToString(bytes)
	return hashCode
}

// SM2Encode sm2公钥加密
func SM2Encode(pubKey string, plaintext string, mode int) (string, error) {
	pubMen, err := x509.ReadPublicKeyFromHex(pubKey)
	if err != nil {
		return "", err
	}
	msg := []byte(plaintext)
	ciphertxt, err := sm2.Encrypt(pubMen, msg, rand.Reader, mode)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(ciphertxt), nil
}

// SM2Decode sm2私钥解密
func SM2Decode(privKey string, data string, mode int) (string, error) {
	priv, err := x509.ReadPrivateKeyFromHex(privKey)
	if err != nil {
		return "", err
	}
	ciphertext, err := hex.DecodeString(data)
	if err != nil {
		return "", err
	}
	plaintext, err := sm2.Decrypt(priv, []byte(ciphertext), mode)
	if err != nil {
		return "", err
	}
	return string(plaintext), nil
}

func GenerateOrderNumber() string {
	// 生成当前日期部分(例如:20231008)
	datePart := time.Now().Format("20060102150405")

	// 生成随机数部分(4位随机数)
	mrand.Seed(time.Now().UnixNano())
	randomPart := fmt.Sprintf("%04d", mrand.Intn(10000))

	// 添加固定前缀
	prefix := "SN"

	// 最终的订单号由前缀、日期和随机数部分组成
	orderNumber := fmt.Sprintf("%s%s%s", prefix, datePart, randomPart)

	return orderNumber
}
func IsNil(x interface{}) bool {
	if x == nil {
		return true
	}
	rv := reflect.ValueOf(x)
	return rv.Kind() == reflect.Ptr && rv.IsNil()
}

type User struct {
	Id    int
	Phone string
}

func GeneratorJwtToken(user User) string {
	// 定义一个用于签名的密钥
	secretKey := []byte(config.GetConf().Jwt.SecretKey)

	// 创建一个MapClaims对象,用于存放自定义的声明信息
	claims := jwt.MapClaims{
		"id":    user.Id,
		"phone": user.Phone,
		"exp":   time.Now().Add(time.Hour * 24).Unix(), // 设置过期时间为24小时后
	}
	// 使用HS256算法创建一个Token对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 使用密钥对Token进行签名,生成JWT字符串
	tokenString, err := token.SignedString(secretKey)
	if err != nil {
		fmt.Println("Failed to create token:", err)
		return ""
	}
	return tokenString
}

type Claims struct {
	Id    int
	Phone string
	jwt.StandardClaims
}

func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
	Claims := &Claims{}
	token, err := jwt.ParseWithClaims(tokenString, Claims, func(token *jwt.Token) (i interface{}, err error) {
		return []byte(config.GetConf().Jwt.SecretKey), nil
	})
	return token, Claims, err
}

func PopLast[T any](slice *[]T) (T, bool) {
	if len(*slice) == 0 {
		var zero T
		return zero, false
	}
	index := len(*slice) - 1
	lastElement := (*slice)[index]
	*slice = (*slice)[:index] // 移除最后一个元素
	return lastElement, true
}

func SonicApiDataToStruct(data interface{}, structInterFace interface{}) (dataStruct interface{}, err error) {
	bytes, err := sonic.Marshal(data)
	if err != nil {
		return nil, err
	}
	err = sonic.Unmarshal(bytes, &structInterFace)
	return structInterFace, err
}

func ContainsString(slice []string, s string) bool {
	for _, item := range slice {
		if item == s {
			return true
		}
	}
	return false
}

// 判断切片是否包含指定字符串
func SliceInStr(s string, slice []string) bool {
	for _, v := range slice {
		if s == v {
			return true
		}
	}
	return false
}

// 判断支付方式支付宝或者微信
func PayType(payChannel int) int {
	switch payChannel {
	case common.PAY_CHANNEL_WECHAT_H5, common.PAY_CHANNEL_WECHAT_JSAPI, common.PAY_CHANNEL_WECHAT_NATIVE, common.PAY_CHANNEL_WECHAT_APP, common.PAY_CHANNEL_WECHAT_MINI:
		return common.PAY_CHANNLE_TYPE_WECHAT
	case common.PAY_CHANNEL_ALIPAY_JSAPI, common.PAY_CHANNEL_ALIPAY_WEB, common.PAY_CHANNEL_ALIPAY_MINI:
		return common.PAY_CHANNLE_TYPE_ZFB
	default:
		return 0
	}
}

// redis控制访问频率
func KeyExistsOrSet(key string, expire int) bool {
	redisClient := redis.GetRedis()

	// 假设Exists方法返回的是int64类型,表示键是否存在(0为不存在,1为存在)
	if exists, err := redisClient.Exists(context.Background(), key).Result(); err == nil && exists == 1 {
		return true
	} else if err != nil {
		// 处理错误情况,可以根据实际情况记录日志或返回错误
		Log(nil, "redis_Exists异常", err)
		return false
	}

	// 如果键不存在,则设置键并返回false
	if err := redisClient.SetEX(context.Background(), key, "", time.Duration(expire)*time.Second).Err(); err != nil {
		// 处理设置键时的错误情况
		Log(nil, "redis_SetEX异常", err)
		return false
	}
	return false
}