package tool

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"github.com/spf13/cast"
	"io"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode"
)

// GetOrderNumber 获取订单号
func GetOrderNumber() string {
	parse, _ := time.Parse(time.DateTime, "2021-04-27 00:00:00")
	hours := time.Now().Sub(parse).Hours()
	day := math.Floor(hours / 24)
	prefix := fmt.Sprintf("%05d", int64(day))
	format := time.Now().Format("") + "%0" + strconv.Itoa(10) + "d"
	n := math.Pow10(10)
	return prefix + fmt.Sprintf(format, rand.New(rand.NewSource(time.Now().UnixNano())).Int63n(int64(n)))
}

// ByteFmt 格式化显示文件大小
func ByteFmt(size int64) string {
	var unitArr = []string{"B", "KB", "MB", "GB", "TB", "EB"}
	if size <= 0 {
		return "unknown"
	}
	fs := float64(size)
	p := int(math.Log(fs) / math.Log(1024))
	val := fs / math.Pow(1024, float64(p))
	_, frac := math.Modf(val)
	if frac > 0 {
		return fmt.Sprintf("%.1f%s", math.Floor(val*10)/10, unitArr[p])
	} else {
		return fmt.Sprintf("%d%s", cast.ToInt(val), unitArr[p])
	}
}

// UniqueArray 切片唯一值
func UniqueArray[T comparable](array []T) []T {
	result := make([]T, 0, len(array))
	temp := map[T]struct{}{}
	for _, item := range array {
		if _, ok := temp[item]; !ok {
			temp[item] = struct{}{}
			result = append(result, item)
		}
	}
	return result
}

// InArray 是否在切片
func InArray[T comparable](item T, array []T) bool {
	for _, s := range array {
		if item == s {
			return true
		}
	}
	return false
}

// ChunkArray 分割切片
func ChunkArray[T comparable](array []T, size int) (ss [][]T) {
	if size <= 0 || len(array) <= size {
		return [][]T{array}
	}
	mod := len(array) % size
	k := len(array) / size
	var end int
	if mod == 0 {
		end = k
	} else {
		end = k + 1
	}
	for i := 0; i < end; i++ {
		if i != k {
			ss = append(ss, array[i*size:(i+1)*size])
		} else {
			ss = append(ss, array[i*size:])
		}
	}
	return
}

// IsChinese 判断字符串是否包含中文
func IsChinese(str string) bool {
	var count int
	for _, v := range str {
		if unicode.Is(unicode.Han, v) {
			count++
			break
		}
	}
	return count > 0
}

// RandInt 随机数
func RandInt(max int) int {
	if max <= 0 {
		return 0
	}
	return rand.Intn(max)
}

// FindIPv4 字符串中查找IP4
func FindIPv4(input string) string {
	partIp := "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])"
	must := partIp + "\\." + partIp + "\\." + partIp + "\\." + partIp
	matchMe := regexp.MustCompile(must)
	return matchMe.FindString(input)
}

// RandString 随机字符串
func RandString(n int) string {
	var letterRunes = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[RandInt(len(letterRunes))]
	}
	return string(b)
}

// IP4ToInt ip4地址转Int类型
func IP4ToInt(ip net.IP) int {
	ipSplit := strings.Split(ip.To4().String(), ".")
	var ipInt = 0
	var pos uint = 24
	for _, ipSeg := range ipSplit {
		tempInt, _ := strconv.Atoi(ipSeg)
		tempInt = tempInt << pos
		ipInt = ipInt | tempInt
		pos -= 8
	}
	return ipInt
}

// IP4IntToString 反序列化Int为IP4地址
func IP4IntToString(ipInt int) string {
	ipSplit := make([]string, 4)
	var length = len(ipSplit)
	buffer := bytes.NewBufferString("")
	for i := 0; i < length; i++ {
		tempInt := ipInt & 0xFF
		ipSplit[length-i-1] = strconv.Itoa(tempInt)
		ipInt = ipInt >> 8
	}
	for i := 0; i < length; i++ {
		buffer.WriteString(ipSplit[i])
		if i < length-1 {
			buffer.WriteString(".")
		}
	}
	return buffer.String()
}

// CompareVersion 比较`x.y.z`类型版本号大小
func CompareVersion(verCurrent, verUpdate string) int {
	versionA := strings.Split(verCurrent, ".")
	versionB := strings.Split(verUpdate, ".")

	for i := len(versionA); i < 4; i++ {
		versionA = append(versionA, "0")
	}
	for i := len(versionB); i < 4; i++ {
		versionB = append(versionB, "0")
	}

	for i := 0; i < 4; i++ {
		version1, _ := strconv.Atoi(versionA[i])
		version2, _ := strconv.Atoi(versionB[i])
		if version1 == version2 {
			continue
		} else if version1 > version2 {
			return 1
		} else {
			return -1
		}
	}

	return 0
}

// GetLocalIP 获取内网本机ipv4地址
func GetLocalIP() (string, error) {
	var address []net.Addr
	eth0Interface, err := net.InterfaceByName("eth0")
	if err == nil {
		address, err = eth0Interface.Addrs()
	} else {
		address, err = net.InterfaceAddrs()
	}
	if err != nil {
		return "", err
	}
	for _, addr := range address {
		if it, ok := addr.(*net.IPNet); ok && !it.IP.IsLoopback() {
			if it.IP.To4() != nil {
				return it.IP.String(), nil
			}
		}
	}
	return "", err
}

// GetInternetIP 获取外网本机ipv4地址
func GetInternetIP() (string, error) {
	resp, err := http.Get("https://api.ipify.org")
	if err != nil {
		resp, err = http.Get("https://icanhazip.com")
		if err != nil {
			return "", err
		}
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	s, readErr := io.ReadAll(resp.Body)
	if readErr != nil {
		return "", readErr
	}
	return string(s), nil
}

// IsExists 检测指定路径文件或者文件夹是否存在
func IsExists(path string) bool {
	_, err := os.Stat(path) //os.Stat获取文件信息
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}

// CheckWebsiteAddr 检查域名是否正确
func CheckWebsiteAddr(addr string) error {
	if 0 == len(addr) {
		return errors.New("url is empty")
	}

	parse, err := url.Parse(addr)
	if err != nil {
		return err
	}

	if "http" != parse.Scheme && "https" != parse.Scheme {
		return errors.New("url Scheme illegal. err:" + parse.Scheme)
	}

	re := regexp.MustCompile(`^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$`)
	u := strings.Split(parse.Host, ":")
	if 2 == len(u) && 0 != len(u[1]) {
		result := re.FindAllStringSubmatch(u[0], -1)
		if result == nil {
			return errors.New("url illegal. err:" + u[0])
		}
		return nil
	}

	result := re.FindAllStringSubmatch(parse.Host, -1)
	if result == nil {
		return errors.New("url illegal. err:" + parse.Host)
	}

	return nil
}

// LoadKeyValConfig 获取本地`a=c\n`类型文件的键值对
func LoadKeyValConfig(text string) map[string]string {
	config := make(map[string]string)
	r := bufio.NewReader(bytes.NewBufferString(text))
	for {
		b, _, err := r.ReadLine()
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}
		s := strings.TrimSpace(string(b))
		index := strings.Index(s, "=")
		if index < 0 || strings.HasPrefix(s, "#") {
			continue
		}
		key := strings.TrimSpace(s[:index])
		if len(key) == 0 {
			continue
		}
		value := strings.TrimSpace(s[index+1:])
		if len(value) == 0 {
			continue
		}
		config[key] = value
	}
	return config
}

// PortIsUse 判断端口是否占用
func PortIsUse(port int) bool {
	_, tcpError := net.DialTimeout("tcp", fmt.Sprintf(":%d", port), time.Millisecond*50)
	udpAddr, _ := net.ResolveUDPAddr("udp4", fmt.Sprintf(":%d", port))
	udpConn, udpError := net.ListenUDP("udp", udpAddr)
	if udpConn != nil {
		defer func() {
			_ = udpConn.Close()
		}()
	}
	return tcpError == nil || udpError != nil
}

// ReplaceInvalidFilenameChars 用下划线替换不合法文件名的字符
func ReplaceInvalidFilenameChars(filename string) string {
	pattern := regexp.MustCompile(`[\\/:*?"<>|{}'()!@#$%^&\[\];,+=~·!¥…()—{}、:“”‘’;,《》。?]`)
	filename = pattern.ReplaceAllString(filename, "_")
	return strings.ReplaceAll(filename, " ", "")
}