package util

import (
	"dsbqj-admin/pkg/hashid"
	"dsbqj-admin/pkg/logger"
	"errors"
	"io"
	"math/rand"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
)

var (
	milliSec int64 = getMilliSec() //毫秒数
)

func init() {
	go func() {
		for {
			time.Sleep(time.Millisecond)
			atomic.StoreInt64(&milliSec, getMilliSec())
		}
	}()
}

// RandStringRunes 返回随机字符串
func RandStringRunes(n int) string {
	var letterRunes = []rune("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

	rand.Seed(time.Now().UnixNano())
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

func RandString(length int) string {
	rand.Seed(time.Now().UnixNano())
	rs := make([]string, length)
	for start := 0; start < length; start++ {
		t := rand.Intn(3)
		if t == 0 {
			rs = append(rs, strconv.Itoa(rand.Intn(10)))
		} else if t == 1 {
			rs = append(rs, string(rand.Intn(26)+65))
		} else {
			rs = append(rs, string(rand.Intn(26)+97))
		}
	}
	return strings.Join(rs, "")
}

func RandomSmsCode(n int) string {
	var letterRunes = []rune("1234567890")

	rand.Seed(time.Now().UnixNano())
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

func RandomInt(min, max int) int {
	rand.Seed(time.Now().UnixNano())
	return rand.Intn(max-min+1) + min
}

func RandFloats(min, max float32, n int) []float32 {
	res := make([]float32, n)
	for i := range res {
		res[i] = min + rand.Float32()*(max-min)
	}
	return res
}

func getMilliSec() int64 {
	return time.Now().UnixNano() / int64(time.Millisecond)
}

func GetNowSecond() int64 {
	return atomic.LoadInt64(&milliSec)
}

func IsSameDay(t1, t2 time.Time) bool {
	y1, m1, d1 := t1.Date()
	y2, m2, d2 := t2.Date()

	return y1 == y2 && m1 == m2 && d1 == d2
}

func TimeStrToTick(t string) int64 {
	loc, err := time.LoadLocation("Asia/Shanghai")
	if err != nil {
		logger.Error("load loc failed, err: %s", err.Error())
		return 0
	}

	tobj, err := time.ParseInLocation("2006-01-02 15:04:05", t, loc)
	if err != nil {
		logger.Error("parse time failed, err: %s", err.Error())
		return 0
	}

	return tobj.UnixNano() / 1e6
}

func subString(str string, start, end int) string {
	rs := []rune(str)
	length := len(rs)

	if start < 0 || start > length {
		panic("start is wrong")
	}

	if end < start || end > length {
		panic("end is wrong")
	}

	return string(rs[start:end])
}

func GetFileDir(path string) string {
	return subString(path, 0, strings.LastIndex(path, "/"))
}

type AarryT interface {
	uint64 | int | uint | string
}

func ExistInArray[T AarryT](arr []T, val T) bool {
	for _, v := range arr {
		if v == val {
			return true
		}
	}
	return false
}

func IndexInArray[T AarryT](arr []T, val T) int {
	for k, v := range arr {
		if v == val {
			return k
		}
	}
	return -1
}

type MapT interface {
	uint64 | int | uint | string
}

func GetMapKeys[T MapT, S MapT](m map[T]S) []T {
	keys := make([]T, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	return keys
}

type Number interface {
	uint64 | int64 | uint32 | int32 | uint | int | uint8 | int8
}

func StringToNumber[T Number](s string) T {
	num, _ := strconv.Atoi(s)
	return T(num)
}

func Exists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// CreatNestedFile 给定path创建文件，如果目录不存在就递归创建
func CreatNestedFile(path string) (*os.File, error) {
	basePath := filepath.Dir(path)
	if !Exists(basePath) {
		err := os.MkdirAll(basePath, 0700)
		if err != nil {
			Log().Warning("无法创建目录，%s", err)
			return nil, err
		}
	}

	return os.Create(path)
}

func DiffIntArr[T AarryT](arr1 []T, arr2 []T) []T {
	res := make([]T, 0)
	tempMap1 := make(map[T]int)
	tempMap2 := make(map[T]int)

	//已数组最长的赋值tempMap1
	if len(arr1) > len(arr2) {
		for _, v := range arr1 { //标记数组内重复数据
			tempMap1[v]++
		}

		for _, v := range arr2 { //标记数组内重复数据
			tempMap2[v]++
		}
	} else {
		for _, v := range arr1 { //标记数组内重复数据
			tempMap2[v]++
		}

		for _, v := range arr2 { //标记数组内重复数据
			tempMap1[v]++
		}
	}

	for k, v := range tempMap1 {
		if tempMap2[k] != v {
			res = append(res, k)
		}
	}

	return res
}

func GetInternalIP() string {
	// 思路来自于Python版本的内网IP获取，其他版本不准确
	conn, err := net.Dial("udp", "8.8.8.8:80")
	if err != nil {
		return ""
	}
	defer conn.Close()

	// udp 面向无连接，所以这些东西只在你本地捣鼓
	res := conn.LocalAddr().String()
	res = strings.Split(res, ":")[0]
	return res
}

func GetExternalIP() (string, error) {
	// 有很多类似网站提供这种服务，这是我知道且正在用的
	// 备用：https://myexternalip.com/raw （cip.cc 应该是够快了，我连手机热点的时候不太稳，其他自己查）
	response, err := http.Get("http://ip.cip.cc")
	if err != nil {
		return "", errors.New("external IP fetch failed, detail:" + err.Error())
	}

	defer response.Body.Close()
	res := ""

	// 类似的API应当返回一个纯净的IP地址
	for {
		tmp := make([]byte, 32)
		n, err := response.Body.Read(tmp)
		if err != nil {
			if err != io.EOF {
				return "", errors.New("external IP fetch failed, detail:" + err.Error())
			}
			res += string(tmp[:n])
			break
		}
		res += string(tmp[:n])
	}

	return strings.TrimSpace(res), nil
}

func IsDir(path string) bool {
	s, err := os.Stat(path)
	if err != nil {

		return false
	}
	return s.IsDir()

}

func ParseProjIdByStr(pid string) (uint, error) {
	nProjID, err := hashid.DecodeHashID(pid, hashid.ProjectID)
	if err != nil {
		return 0, err
	}

	return nProjID, nil
}
