package db

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
	"math/big"
	"reflect"
	"strconv"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"

	mssql "github.com/microsoft/go-mssqldb"
)

const (
	jsMaxSafeInteger int64  = 9007199254740991
	jsMinSafeInteger int64  = -9007199254740991
	jsMaxSafeUint    uint64 = 9007199254740991
)

var (
	jsMaxSafeBigInt = big.NewInt(jsMaxSafeInteger)
	jsMinSafeBigInt = big.NewInt(jsMinSafeInteger)
)

// normalizeQueryValue normalizes driver-returned values for UI/JSON transport.
// 当前主要处理 []byte：如果是可读文本则转为 string，否则转为十六进制字符串，避免前端出现“空白值”。
func normalizeQueryValue(v interface{}) interface{} {
	return normalizeQueryValueWithDBType(v, "")
}

func normalizeQueryValueWithDBType(v interface{}, databaseTypeName string) interface{} {
	if tm, ok := v.(time.Time); ok {
		return normalizeTemporalValueForDisplay(tm, databaseTypeName)
	}
	if b, ok := v.([]byte); ok {
		return bytesToDisplayValue(b, databaseTypeName)
	}
	return normalizeCompositeQueryValue(v)
}

func normalizeTemporalValueForDisplay(value time.Time, databaseTypeName string) interface{} {
	if value.IsZero() {
		if zeroValue, ok := zeroTemporalDisplayValue(databaseTypeName); ok {
			return zeroValue
		}
	}
	return value.Format(time.RFC3339Nano)
}

func zeroTemporalDisplayValue(databaseTypeName string) (string, bool) {
	typeName := strings.ToUpper(strings.TrimSpace(databaseTypeName))
	if typeName == "" {
		return "0000-00-00 00:00:00", true
	}

	switch {
	case strings.Contains(typeName, "TIMESTAMP") || strings.Contains(typeName, "DATETIME"):
		return "0000-00-00 00:00:00", true
	case typeName == "DATE" || typeName == "NEWDATE":
		return "0000-00-00", true
	case strings.Contains(typeName, "TIME"):
		return "00:00:00", true
	case strings.Contains(typeName, "YEAR"):
		return "0000", true
	default:
		return "", false
	}
}

func normalizeCompositeQueryValue(v interface{}) interface{} {
	if v == nil {
		return nil
	}

	switch typed := v.(type) {
	case []interface{}:
		items := make([]interface{}, len(typed))
		for i, item := range typed {
			items[i] = normalizeQueryValue(item)
		}
		return items
	case map[string]interface{}:
		out := make(map[string]interface{}, len(typed))
		for key, value := range typed {
			out[key] = normalizeQueryValue(value)
		}
		return out
	case json.Number:
		return normalizeJSONNumberForJS(typed)
	}

	rv := reflect.ValueOf(v)
	switch rv.Kind() {
	case reflect.Pointer:
		if rv.IsNil() {
			return nil
		}
		return normalizeQueryValue(rv.Elem().Interface())
	case reflect.Map:
		if rv.IsNil() {
			return nil
		}
		out := make(map[string]interface{}, rv.Len())
		iter := rv.MapRange()
		for iter.Next() {
			out[mapKeyToString(iter.Key().Interface())] = normalizeQueryValue(iter.Value().Interface())
		}
		return out
	case reflect.Slice, reflect.Array:
		// []byte 在上层已单独处理，这里保留对其它切片/数组的递归规整。
		if rv.Kind() == reflect.Slice && rv.IsNil() {
			return nil
		}
		size := rv.Len()
		items := make([]interface{}, size)
		for i := 0; i < size; i++ {
			items[i] = normalizeQueryValue(rv.Index(i).Interface())
		}
		return items
	case reflect.Struct:
		// 部分驱动（如 Kingbase）会返回复杂结构体值，直接透传会导致前端渲染和比较开销激增。
		// 统一降级为可读字符串，避免对象深层序列化触发 UI 卡顿。
		if tm, ok := v.(time.Time); ok {
			return normalizeTemporalValueForDisplay(tm, "")
		}
		if stringer, ok := v.(fmt.Stringer); ok {
			return stringer.String()
		}
		return fmt.Sprintf("%v", v)
	default:
		return normalizeUnsafeIntegerForJS(rv, v)
	}
}

func normalizeJSONNumberForJS(n json.Number) interface{} {
	text := strings.TrimSpace(n.String())
	if text == "" {
		return ""
	}

	if integer, ok := parseJSONInteger(text); ok {
		if integer.Cmp(jsMaxSafeBigInt) > 0 || integer.Cmp(jsMinSafeBigInt) < 0 {
			return text
		}
		return integer.Int64()
	}

	if f, err := n.Float64(); err == nil {
		return f
	}
	return text
}

func parseJSONInteger(text string) (*big.Int, bool) {
	if text == "" {
		return nil, false
	}
	start := 0
	if text[0] == '+' || text[0] == '-' {
		if len(text) == 1 {
			return nil, false
		}
		start = 1
	}
	for i := start; i < len(text); i++ {
		if text[i] < '0' || text[i] > '9' {
			return nil, false
		}
	}
	value, ok := new(big.Int).SetString(text, 10)
	if !ok {
		return nil, false
	}
	return value, true
}

func mapKeyToString(key interface{}) string {
	if key == nil {
		return "null"
	}
	if s, ok := key.(string); ok {
		return s
	}
	return fmt.Sprintf("%v", key)
}

func bytesToDisplayValue(b []byte, databaseTypeName string) interface{} {
	if b == nil {
		return nil
	}
	if len(b) == 0 {
		return ""
	}

	dbType := strings.ToUpper(strings.TrimSpace(databaseTypeName))
	if isSQLServerUniqueIdentifierType(dbType) {
		var guid mssql.UniqueIdentifier
		if err := guid.Scan(b); err == nil {
			return guid.String()
		}
	}
	if isBitLikeDBType(dbType) {
		if u, ok := bytesToUint64(b); ok {
			// JS number precision is limited; keep large bitmasks as string.
			if u <= jsMaxSafeUint {
				return int64(u)
			}
			return fmt.Sprintf("%d", u)
		}
	}

	if utf8.Valid(b) {
		s := string(b)
		if isMostlyPrintable(s) {
			return s
		}
	}

	// Fallback: some drivers return BIT(1) as []byte{0} / []byte{1} without type info.
	if dbType == "" && len(b) == 1 && (b[0] == 0 || b[0] == 1) {
		return int64(b[0])
	}

	return bytesToReadableString(b)
}

func bytesToReadableString(b []byte) interface{} {
	if b == nil {
		return nil
	}
	if len(b) == 0 {
		return ""
	}
	return "0x" + hex.EncodeToString(b)
}

func isSQLServerUniqueIdentifierType(typeName string) bool {
	return typeName == "UNIQUEIDENTIFIER"
}

func isBitLikeDBType(typeName string) bool {
	if typeName == "" {
		return false
	}
	switch typeName {
	case "BIT", "VARBIT":
		return true
	default:
	}
	return strings.HasPrefix(typeName, "BIT")
}

func bytesToUint64(b []byte) (uint64, bool) {
	if len(b) == 0 || len(b) > 8 {
		return 0, false
	}
	var u uint64
	for _, v := range b {
		u = (u << 8) | uint64(v)
	}
	return u, true
}

func normalizeUnsafeIntegerForJS(rv reflect.Value, original interface{}) interface{} {
	switch rv.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		n := rv.Int()
		if n > jsMaxSafeInteger || n < jsMinSafeInteger {
			return strconv.FormatInt(n, 10)
		}
		return original
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		u := rv.Uint()
		if u > jsMaxSafeUint {
			return strconv.FormatUint(u, 10)
		}
		return original
	default:
		return original
	}
}

func isMostlyPrintable(s string) bool {
	if s == "" {
		return true
	}

	total := 0
	printable := 0
	for _, r := range s {
		total++
		switch r {
		case '\n', '\r', '\t':
			printable++
			continue
		default:
		}
		if unicode.IsPrint(r) {
			printable++
		}
	}

	// 允许少量不可见字符，避免把正常文本误判为二进制。
	return printable*100 >= total*90
}
