package utils

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/jordan-wright/email"
	"github.com/kataras/iris/v12"
	"github.com/shopspring/decimal"
	"github.com/sirupsen/logrus"
	"io"
	"main/config"
	"main/database"
	"main/model"
	"math/rand"
	"net/http"
	"net/smtp"
	"os"
	"path"
	"reflect"
	"regexp"
	"runtime"
	"strings"
	"time"
	"unsafe"
)

// FormatRes 格式化result
func FormatRes(code int, msg string, data interface{}) ResponseBean {
	return ResponseBean{
		Code: code,
		Msg:  msg,
		Data: data,
	}
}

// GetEnvDefault 获取带默认值环境变量
func GetEnvDefault(name string, defaultVal string) string {
	val, ok := os.LookupEnv(name)
	if ok {
		return val
	} else {
		return defaultVal
	}
}

// FileRead 读取文件，返回行列表
func FileRead(file string, condition bool) ([]string, error) {
	f, err := os.Open(file)
	var res []string
	if err != nil {
		return []string{}, err
	}
	defer func(f *os.File) {
		err = f.Close()
		if err != nil {
			return
		}
	}(f)
	reader := bufio.NewReader(f)
	for {
		line, _, err := reader.ReadLine()
		if err != nil {
			break
		}
		if condition && len(line) > 1 {
			res = append(res, string(line))
		}
	}
	return res, nil
}

func SendEmail(receiver []string, subject string, content string) error {
	emailConfig := config.Config.Email
	sender := fmt.Sprintf("ylsa <%s>", emailConfig.Username)
	message := email.NewEmail()
	message.From = sender
	message.To = receiver
	message.Subject = subject
	message.Text = []byte(content)
	logrus.Infoln("发送邮件：", sender, receiver)
	err := message.Send("smtp.163.com:25", smtp.PlainAuth("", emailConfig.Username, emailConfig.Password, "smtp.163.com"))
	if err != nil {
		return err
	}
	return nil
}

// DataIsNil 判断数据是否为空
func DataIsNil(arg interface{}) bool {
	if reflect.ValueOf(arg).Kind().String() == "ptr" || reflect.ValueOf(arg).Kind().String() == "slice" {
		if reflect.ValueOf(arg).IsValid() {
			return true
		}
	} else {
		if reflect.ValueOf(arg).IsZero() {
			return true
		}
	}
	return false
}

// ErrHandle 错误处理
func ErrHandle(ctx iris.Context, err error) bool {
	if err != nil {
		pc, _, _, _ := runtime.Caller(1)
		f := runtime.FuncForPC(pc).Name()
		db := database.GetInstance().GetMysqlDb()
		if err1 := db.Create(&model.SysError{
			Username:  GetLoginUser(ctx).Username,
			Time:      time.Now(),
			Function:  f,
			ErrorInfo: err.Error(),
		}).Error; err1 != nil {
			logrus.Errorln("sql执行失败：", err1.Error())
		}
		ctx.StatusCode(iris.StatusInternalServerError)
		ctx.SetErr(err)
		return true
	}
	return false
}

// NewKey 取n位随机数
func NewKey(n int) string {
	const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890()-+*/~"
	var src = rand.NewSource(time.Now().UnixNano())
	const (
		// 6 bits to represent a letter index
		letterIdBits = 6
		// All 1-bits as many as letterIdBits
		letterIdMask = 1<<letterIdBits - 1
		letterIdMax  = 63 / letterIdBits
	)
	b := make([]byte, n)
	// A rand.Int63() generates 63 random bits, enough for letterIdMax letters!
	for i, cache, remain := n-1, src.Int63(), letterIdMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdMax
		}
		if idx := int(cache & letterIdMask); idx < len(letters) {
			b[i] = letters[idx]
			i--
		}
		cache >>= letterIdBits
		remain--
	}
	return *(*string)(unsafe.Pointer(&b))
}

// CheckListItem 判断item是否在list内
func CheckListItem[T comparable](data []T, item T) bool {
	for _, value := range data {
		if value == item {
			return true
		}
	}
	return false
}

// FileIsExist 判断路径是否存在
func FileIsExist(path string) bool {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return false
	}
	return true
}

// Round float取小数
func Round(num float64, n int32) float64 {
	res, _ := decimal.NewFromFloat(num).Round(n).Float64()
	return res
}

// GetFileType 获取文件类型
func GetFileType(f string) string {
	fileSuffix := path.Ext(f)
	fileSuffix = strings.ToLower(fileSuffix)
	patternFileSuffix := fmt.Sprintf("^%s,|,%s,|,%s$|%s", fileSuffix, fileSuffix, fileSuffix, fileSuffix)
	regFileSuffix := regexp.MustCompile(patternFileSuffix)
	for _, v := range SysSettings {
		if strings.HasPrefix(v.Name, "fileType:") && regFileSuffix.MatchString(v.Value) {
			return strings.Split(v.Name, "fileType:")[1]
		}
	}
	//switch fileSuffix {
	//case ".png", ".jpg", ".jpeg", ".bmp", ".gif":
	//	return "image"
	//case ".mp4", ".m2v", ".mkv", ".rmvb", ".avi", ".flv", ".mov", ".m4v", ".wmv", ".f4v":
	//	return "video"
	//case ".mp3", ".wav", ".flac":
	//	return "audio"
	//case ".zip", ".rar", ".7z":
	//	return "zip"
	//}
	return "doc"
}

// IntAbs 数字取绝对值
func IntAbs(i int) int {
	if i < 0 {
		return -i
	} else {
		return i
	}
}

// Reverse 字符串反转
func Reverse(s string) string {
	a := []rune(s)
	for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
		a[i], a[j] = a[j], a[i]
	}
	return string(a)
}

func GetRequestIp(ctx iris.Context) string {
	realIp := ctx.Request().Header.Get("X-Real-IP")
	if realIp != "" {
		return realIp
	}
	ip := ctx.RemoteAddr()
	return ip
}

func GetIpLocation(ip string) (string, error) {
	var ipLocation resIpLocation
	data, err := http.Get(fmt.Sprintf("https://opendata.baidu.com/api.php?query=%s&co=&resource_id=6006&oe=utf8", ip))
	if err != nil {
		logrus.Errorln(ip, "ip地址请求失败", err)
		return "", err
	}
	defer func(Body io.ReadCloser) {
		err = Body.Close()
		if err != nil {
			return
		}
	}(data.Body)
	body, err := io.ReadAll(data.Body)
	if err != nil {
		logrus.Errorln(ip, "获取IP地址请求读取失败：", err)
		return "", err
	}
	err = json.Unmarshal(body, &ipLocation)
	if err != nil {
		logrus.Errorln(ip, "IP地址请求结果解析失败：", err)
		return "", err
	}
	if len(ipLocation.Data) == 0 {
		logrus.Errorln(ip, "ip地址无效：")
		return "", errors.New("ip地址无效")
	}
	return ipLocation.Data[0].Location, nil
}
