package tools

import (
	"errors"
	"fmt"
	"gorm.io/gorm"
	"log"
	"math"
	"reflect"
	"runtime"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/spf13/cast"
	"golang.org/x/crypto/bcrypt"
)

func CompareHashAndPassword(e string, p string) (bool, error) {
	err := bcrypt.CompareHashAndPassword([]byte(e), []byte(p))
	if err != nil {
		return false, err
	}
	return true, nil
}

// Assert 条件断言
// 当断言条件为 假 时触发 panic
// 对于当前请求不会再执行接下来的代码，并且返回指定格式的错误信息和错误码
func Assert(condition bool, msg string, code ...int) {
	if !condition {
		statusCode := 200
		if len(code) > 0 {
			statusCode = code[0]
		}
		panic("CustomError#" + strconv.Itoa(statusCode) + "#" + msg)
	}
}

// HasError 错误断言
// 当 error 不为 nil 时触发 panic
// 对于当前请求不会再执行接下来的代码，并且返回指定格式的错误信息和错误码
// 若 msg 为空，则默认为 error 中的内容
func HasError(err error, msg string, code ...int) {
	if err != nil {
		statusCode := 200
		if len(code) > 0 {
			statusCode = code[0]
		}
		if msg == "" {
			msg = err.Error()
		}
		_, file, line, _ := runtime.Caller(1)
		log.Printf("%s:%v error: %#v", file, line, err)
		panic("CustomError#" + strconv.Itoa(statusCode) + "#" + msg)
	}
}

// GenerateMsgIDFromContext 生成msgID
func GenerateMsgIDFromContext(c *gin.Context) string {
	var msgID string
	data, ok := c.Get("msgID")
	if !ok {
		msgID = uuid.New().String()
		c.Set("msgID", msgID)
		return msgID
	}
	msgID = cast.ToString(data)
	return msgID
}

// GetOrm 获取orm连接
func GetOrm(c *gin.Context) (*gorm.DB, error) {
	msgID := GenerateMsgIDFromContext(c)
	idb, exist := c.Get("db")
	if !exist {
		return nil, errors.New(fmt.Sprintf("msgID[%s], db connect not exist", msgID))
	}
	switch idb.(type) {
	case *gorm.DB:
		//新增操作
		return idb.(*gorm.DB), nil
	default:
		return nil, errors.New(fmt.Sprintf("msgID[%s], db connect not exist", msgID))
	}
}

// RoundFloat64 将float64类型四舍五入取整
func RoundFloat64(input float64) int {
	rounded := int(math.Round(input))
	return rounded
}

// ConvertTimeToString 将time类型转换为string
func ConvertTimeToString(t time.Time) string {
	if t.IsZero() {
		return "" // 零值或 nil 返回空字符串
	}
	return t.Format("2006-01-02 15:04:05")
}

// ConvertTimeToStringByPoint 将time类型转换为string
func ConvertTimeToStringByPoint(t *time.Time) string {
	if t == nil {
		return "" // 零值或 nil 返回空字符串
	}
	if t.IsZero() {
		return "" // 零值或 nil 返回空字符串
	}
	return t.Format("2006-01-02 15:04:05")
}

// RoundToTwoDecimalPlaces 将float64类型四舍五入保留2位小数
func RoundToTwoDecimalPlaces(num float64) float64 {
	return math.Round(num*100) / 100
}

// Uint32SliceContains 检查 uint32 切片中是否包含特定的值
func Uint32SliceContains(slice []uint32, val uint32) bool {
	for _, item := range slice {
		if item == val {
			return true
		}
	}
	return false
}

// StringSliceContains 检查 string 切片中是否包含特定的值
func StringSliceContains(slice []string, val string) bool {
	for _, item := range slice {
		if item == val {
			return true
		}
	}
	return false
}

// RoundFloat 保留 float64 两位小数
func RoundFloat(f float64) float64 {
	return math.Round(f*100) / 100
}

// RoundFloatFields 递归处理结构体中的 float64 字段，四舍五入保留两位小数
func RoundFloatFields(v interface{}) {
	val := reflect.ValueOf(v).Elem()

	for i := 0; i < val.NumField(); i++ {
		field := val.Field(i)
		switch field.Kind() {
		case reflect.Float64:
			// 对 float64 类型进行四舍五入
			field.SetFloat(RoundFloat(field.Float()))
		case reflect.Struct:
			// 递归处理子结构体
			RoundFloatFields(field.Addr().Interface())
		case reflect.Slice:
			// 对于 slice 中的结构体，逐个处理
			for j := 0; j < field.Len(); j++ {
				if field.Index(j).Kind() == reflect.Struct {
					RoundFloatFields(field.Index(j).Addr().Interface())
				}
			}
		}
	}
}
