package utility

import (
	"fmt"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"unicode/utf8"
	"unsafe"

	"go.uber.org/zap"

	log "github.com/go-admin-team/go-admin-core/logger"
	"github.com/shopspring/decimal"
)

// 获取不超过指定长度的字段
func StringCut(s string, maxLength int) string {
	if len(s) > maxLength {
		return s[:maxLength] // 获取前 maxLength 个字符
	}
	return s // 如果长度不足，返回原字符串
}

// FloatToStringZero 去掉多余的小数0,比如0.0245000返回0.0245,return string
func FloatToStringZero(num string) string {
	de, _ := decimal.NewFromString(num)
	str := de.String()
	return str
}

// IntTostring int to string
func IntTostring(num int) string {
	if num == 0 {
		return "0"
	}
	return strconv.Itoa(num)
}

// StringLen 获取字符串真实长度
func StringLen(s string) int {
	return len([]rune(s))
}

// StringIsNil 检测字符串长度是否为0
func StringIsNil(str string) bool {
	if len(str) == 0 {
		return true
	}
	return len(strings.Trim(str, " ")) == 0
}

// StringTrim 去前后空格
func StringTrim(str string) string {
	return strings.Trim(str, " ")
}

// StringAsFloat tries to convert a string to float, and if it can't, just returns zero
// 尝试将字符串转换为浮点数，如果不能，则返回0
func StringAsFloat(s string) float64 {
	if len(s) == 0 {
		return 0.0
	}
	if f, err := strconv.ParseFloat(s, 64); err == nil {
		return f
	}
	return 0.0
}

func StringAsDecimal(s string) decimal.Decimal {
	if len(s) == 0 {
		return decimal.Zero
	}
	f, err := decimal.NewFromString(s)
	if err != nil {
		log.Error("字符串转化decimal失败: ", zap.Error(err))
		return decimal.Zero
	}
	return f
}

func StringAsFloatCut(s string, size int32) float64 {
	if len(s) == 0 {
		return 0
	}
	f, err := decimal.NewFromString(s)
	if err != nil {
		log.Error("字符串转化decimal失败: ", zap.Error(err))
		return 0
	}
	d, _ := f.Truncate(size).Float64()
	return d
}

// FloatToString 按保留的小数点位数,去掉多余的小数,return string
func FloatToString(num float64, size int32) string {
	if num == 0 {
		return getZero(size)
	}
	de := decimal.NewFromFloat(num)
	str := de.Truncate(size).String()
	return str
}

func getZero(size int32) string {
	switch size {
	case 0:
		return "0"
	case 1:
		return "0.0"
	case 2:
		return "0.00"
	case 3:
		return "0.000"
	case 4:
		return "0.0000"
	case 5:
		return "0.00000"
	case 6:
		return "0.000000"
	}
	return "0"
}

func GetGearNumStr(size int32) string {
	switch size {
	case 0:
		return "1"
	case 1:
		return "0.1"
	case 2:
		return "0.01"
	case 3:
		return "0.001"
	case 4:
		return "0.0001"
	case 5:
		return "0.00001"
	case 6:
		return "0.000001"
	case 7:
		return "0.0000001"
	case 8:
		return "0.00000001"
	}
	return "0.1"
}

func FloatToStr(num float64) string {
	de := decimal.NewFromFloat(num)
	str := de.String()
	return str
}

// StringAsInteger returns the integer value extracted from string, or zero
func StringAsInteger(s string) int {
	if s == "" {
		return 0
	}
	if i, err := strconv.ParseInt(s, 10, 32); err == nil {
		return int(i)
	}
	return 0
}

// StringAsInt64 returns the int64 value extracted from string, or zero
func StringAsInt64(s string) int64 {
	if s == "" {
		return 0
	}
	if i, err := strconv.ParseInt(s, 10, 64); err == nil {
		return i
	}
	return 0
}

func StringAsInt32(s string) int32 {
	if s == "" {
		return 0
	}
	if i, err := strconv.ParseInt(s, 10, 64); err == nil {
		return int32(i)
	}
	return 0
}

// StringToDecimal String To Decimal
func StringToDecimal(val string) decimal.Decimal {
	cleanedNum := strings.TrimRight(val, "\x00")         // 去除空字符
	cleanedNum = strings.TrimSpace(cleanedNum)           // 去除空格
	cleanedNum = strings.ReplaceAll(cleanedNum, ",", "") // 去除逗号
	d, err := decimal.NewFromString(cleanedNum)
	if err != nil {
		return decimal.Zero
	}
	return d
}

// StringToInt String =>  int
func StringToInt(val string) int {
	i, err := strconv.Atoi(val)
	if err != nil {
		return 0
	}
	return i
}

// StringToFloat64 String =>  Float64
func StringToFloat64(val string) float64 {
	d, err := strconv.ParseFloat(val, 64)
	if err != nil {
		return 0
	}
	return d
}

// ToLower 返回小写字符串
func ToLower(str string) string {
	return strings.ToLower(str)
}

// ToUpper 返回大写字符串
func ToUpper(str string) string {
	return strings.ToUpper(str)
}

// IntToString int to string
func IntToString(num int) string {
	if num == 0 {
		return "0"
	}
	return strconv.Itoa(num)
}

// CheckPhone returns true if a given sequence has between 9 and 14 digits
func CheckPhone(phone string, acceptEmpty bool) bool {
	phone = OnlyDigits(phone)

	return (acceptEmpty && (phone == "")) || ((len([]rune(phone)) >= 9) && (len([]rune(phone)) <= 14))
}

// OnlyDigits returns only the numbers from the given string, after strip all the rest ( letters, spaces, etc. )
func OnlyDigits(sequence string) string {
	if utf8.RuneCountInString(sequence) > 0 {
		re, _ := regexp.Compile(`[\D]`)

		sequence = re.ReplaceAllString(sequence, "")
	}

	return sequence
}

// CheckEmail returns true if the given sequence is a valid email address
// See https://tools.ietf.org/html/rfc2822#section-3.4.1 for details about email address anatomy
func CheckEmail(email string) bool {
	if email == "" {
		return false
	}

	re := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")

	return re.MatchString(email)
}

// IsNumericType checks if an interface's concrete type corresponds to some of golang native numeric types
func IsNumericType(x interface{}) bool {
	switch x.(type) {
	case uint:
		return true
	case uint8: // Or byte
		return true
	case uint16:
		return true
	case uint32:
		return true
	case uint64:
		return true
	case int:
		return true
	case int8:
		return true
	case int16:
		return true
	case int32:
		return true
	case float32:
		return true
	case float64:
		return true
	case complex64:
		return true
	case complex128:
		return true
	default:
		return false
	}
}

// B2S converts byte slice to a string without memory allocation.
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
//
// Note it may break if string and/or slice header will change
// in the future go versions.
func B2S(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

// S2B converts string to a byte slice without memory allocation.
// Note it may break if string and/or slice header will change
// in the future go versions.
func S2B(s string) (b []byte) {
	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
	bh.Data = sh.Data
	bh.Len = sh.Len
	bh.Cap = sh.Len
	return b
}

// Int64ToString int64 转到string
func Int64ToString(num int64) string {
	if num == 0 {
		return ""
	}
	return strconv.FormatInt(num, 10)
}

// Float64ToString Float64 转 string
// prec: 小数位数 -1 为全保留 与 FloatCutStr 不同的是 这里会四舍五入
func Float64ToString(num float64, prec int32) string {
	return strconv.FormatFloat(num, 'f', int(prec), 64)
}

// SliceRemoveDuplicates 除去[]string数组重复的数据
func SliceRemoveDuplicates(slice []string) []string {
	sort.Strings(slice)
	i := 0
	var j int
	for {
		if i >= len(slice)-1 {
			break
		}

		for j = i + 1; j < len(slice) && slice[i] == slice[j]; j++ {
		}

		slice = append(slice[:i+1], slice[j:]...)
		i++
	}

	return slice
}

func GetSymbolIdStr(coinId, currId string) string {
	return fmt.Sprintf("%v/%v", coinId, currId)
}

func GetSymbolCoinIdAndCurrId(symbolIdStr string) (string, string) {
	l := strings.Split(symbolIdStr, "/")
	return l[0], l[1]
}

// CheckIdCard 检验身份证
func CheckIdCard(card string) bool {
	// 18位身份证 ^(\d{17})([0-9]|X)$
	// 匹配规则
	// (^\d{15}$) 15位身份证
	// (^\d{18}$) 18位身份证
	// (^\d{17}(\d|X|x)$) 18位身份证 最后一位为X的用户
	regRuler := "(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)"

	// 正则调用规则
	reg := regexp.MustCompile(regRuler)

	// 返回 MatchString 是否匹配
	return reg.MatchString(card)
}
func FilteredSQLInject(to_match_str string) bool {
	//过滤 ‘
	//ORACLE 注解 --  /**/
	//关键字过滤 update ,delete
	// 正则的字符串, 不能用 " " 因为" "里面的内容会转义
	str := `(?:')|(?:--)|(/\*(?:.|[\n\r])*?\*/)|((select|update|and|or|delete|insert|trancate|char|chr|into|substr|ascii|declare|exec|count|master|into|drop|execute))`
	re, err := regexp.Compile(str)
	if err != nil {
		return false
	}
	return re.MatchString(to_match_str)
}
func TrimHtml(src string) string {
	//将HTML标签全转换成小写
	re, _ := regexp.Compile("\\<[\\S\\s]+?\\>")
	src = re.ReplaceAllStringFunc(src, strings.ToLower)
	//去除STYLE
	re, _ = regexp.Compile("\\<style[\\S\\s]+?\\</style\\>")
	src = re.ReplaceAllString(src, "")
	//去除SCRIPT
	re, _ = regexp.Compile("\\<script[\\S\\s]+?\\</script\\>")
	src = re.ReplaceAllString(src, "")
	//去除所有尖括号内的HTML代码，并换成换行符
	re, _ = regexp.Compile("\\<[\\S\\s]+?\\>")
	src = re.ReplaceAllString(src, "\n")
	//去除连续的换行符
	re, _ = regexp.Compile("\\s{2,}")
	src = re.ReplaceAllString(src, "\n")
	return strings.TrimSpace(src)
}

// ReplaceImages 过滤掉图片
func ReplaceImages(htmls string) string {
	result := htmls
	regx := `<img[^>]*src[="'s]+[^.]*/([^.]+).[^"']+["']?[^>]*>`
	var imgRE = regexp.MustCompile(regx)
	imgs := imgRE.FindAllStringSubmatch(htmls, -1)
	for i := range imgs {
		v := imgs[i]
		result = strings.ReplaceAll(result, v[0], "")
	}
	return result
}

// KeySubString 文章搜索专用
func KeySubString(key, value string, leng int) string {
	b := ReplaceImages(value)
	pos := strings.Index(b, key)
	if pos < 0 {
		return ""
	}
	start := b[0:pos]
	end := b[pos:]
	out := Substr(end, leng)
	return start + out
}

// Substr 截取字符串
func Substr(s string, l int) string {
	if len(s) <= l {
		return s
	}
	ss, sl, rl, rs := "", 0, 0, []rune(s)
	for _, r := range rs {
		rint := int(r)
		if rint < 128 {
			rl = 1
		} else {
			rl = 2
		}

		if sl+rl > l {
			break
		}
		sl += rl
		ss += string(r)
	}
	return ss
}

/*
根据字符串获取小数位数
*/
func GetPrecision(value string) int {
	// 去掉末尾的多余零
	value = strings.TrimRight(value, "0")

	// 找到小数点的位置
	decimalPos := strings.Index(value, ".")

	if decimalPos == -1 {
		// 如果没有小数点，说明是整数，精度为0
		return 0
	}

	// 计算小数点后的位数
	return len(value) - decimalPos - 1
}

// 替换字符串后缀
func ReplaceSuffix(text, oldSuffix, newSuffix string) string {
	if strings.HasSuffix(text, oldSuffix) {
		return text[:len(text)-len(oldSuffix)] + newSuffix
	}
	return text
}
