package typekit

//package main

import (
	"fmt"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"unsafe"
)

const (
	colorReset  = "\033[0m"
	colorStruct = "\033[34m\033[7m"
	colorField  = "\033[32m"
	colorMap    = "\033[33m\033[7m"
	colorSlice  = "\033[35m\033[7m"
	colorValue  = "\033[31m"
	colorKey    = "\033[36m"
	symbolVert  = "│  "
	symbolLast  = "└─ "
	symbolMid   = "├─ "
	symbolEmpty = "   "
)

type PrintOptions struct {
	ShowTypes bool
}

func PrettyPrint(v interface{}, opts ...PrintOptions) string {
	option := PrintOptions{ShowTypes: true}
	if len(opts) > 0 {
		option = opts[0]
	}
	return prettyPrint(reflect.ValueOf(v), 0, make(map[uintptr]bool), option, []bool{}).String()
}

type lines []string

func (l lines) String() string {
	return strings.Join(l, "\n")
}

func buildPrefix(indent int, isLasts []bool) string {
	var prefix strings.Builder
	for i := 0; i < indent; i++ {
		if i < len(isLasts) {
			if isLasts[i] {
				prefix.WriteString(symbolEmpty)
			} else {
				prefix.WriteString(symbolVert)
			}
		}
	}
	return prefix.String()
}

func prettyPrint(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, isLasts []bool) lines {
	original := v
	for {
		switch v.Kind() {
		case reflect.Ptr:
			if v.IsNil() {
				return lines{colorValue + "nil" + colorReset}
			}
			ptr := v.Pointer()
			if visited[ptr] {
				return lines{fmt.Sprintf("%s&%p%s", colorValue, v.Interface(), colorReset)}
			}
			visited[ptr] = true
			original = v
			v = v.Elem()
		case reflect.Interface:
			if v.IsNil() {
				return lines{colorValue + "nil" + colorReset}
			}
			v = v.Elem()
		default:
			goto END_DEREF
		}
	}
END_DEREF:

	switch v.Kind() {
	case reflect.Struct:
		return printStruct(v, indentLevel, visited, opts, isLasts)
	case reflect.Map:
		return printMap(v, indentLevel, visited, opts, isLasts)
	case reflect.Slice, reflect.Array:
		return printSlice(v, indentLevel, visited, opts, isLasts)
	case reflect.String:
		return lines{fmt.Sprintf("%s%q%s", colorValue, v.String(), colorReset)}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return lines{fmt.Sprintf("%s%d%s", colorValue, v.Int(), colorReset)}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return lines{fmt.Sprintf("%s%d%s", colorValue, v.Uint(), colorReset)}
	case reflect.Float32, reflect.Float64:
		// 使用精确的字符串表示，保留必要精度
		str := strconv.FormatFloat(v.Float(), 'f', -1, 64)
		// 移除多余的末尾零和小数点
		if strings.Contains(str, ".") {
			str = strings.TrimRight(str, "0")
			str = strings.TrimRight(str, ".")
		}
		return lines{fmt.Sprintf("%s%s%s", colorValue, str, colorReset)}
	case reflect.Bool:
		return lines{fmt.Sprintf("%s%t%s", colorValue, v.Bool(), colorReset)}
	default:
		if original.Kind() == reflect.Ptr {
			return lines{fmt.Sprintf("%s%v%s", colorValue, original.Interface(), colorReset)}
		}
		//return lines{fmt.Sprintf("%s%v%s", colorValue, v.Interface(), colorReset)}
		return lines{formatUnexported(v, original)}
	}
}

// 新增辅助函数处理未导出字段
func formatUnexported(v, original reflect.Value) string {
	var value interface{}
	var canShow bool

	// 尝试通过不同方式安全获取值
	switch {
	case v.CanInterface():
		value = v.Interface()
		canShow = true
	case original.Kind() == reflect.Ptr && original.CanInterface():
		value = original.Interface()
		canShow = true
	default:
		canShow = false
	}

	// 特殊处理指针类型
	if v.Kind() == reflect.Ptr && !v.IsNil() {
		return fmt.Sprintf("%s&%p%s", colorValue, unsafe.Pointer(v.Pointer()), colorReset)
	}

	// 构造显示字符串
	if canShow {
		return fmt.Sprintf("%s%v%s", colorValue, value, colorReset)
	}
	return fmt.Sprintf("%s<unexported>%s", colorValue, colorReset)
}

func printStruct(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, parentLasts []bool) lines {
	var header string
	if opts.ShowTypes {
		header = fmt.Sprintf("%s%s%s", colorStruct, v.Type().String(), colorReset)
	} else {
		header = " ---"
	}

	result := lines{header}
	fieldCount := v.NumField()

	for i := 0; i < fieldCount; i++ {
		field := v.Type().Field(i)
		// 跳过未导出字段
		if !field.IsExported() {
			continue
		}
		fieldValue := v.Field(i)
		isLast := i == fieldCount-1

		prefix := buildPrefix(indentLevel, parentLasts)
		currentLasts := append(parentLasts, isLast)

		var symbol string
		if isLast {
			symbol = symbolLast
		} else {
			symbol = symbolMid
		}

		fieldLines := prettyPrint(fieldValue, indentLevel+1, visited, opts, currentLasts)
		fieldHeader := fmt.Sprintf("%s%s%s%s: ",
			prefix,
			symbol,
			colorField,
			field.Name,
		)

		if len(fieldLines) == 0 {
			result = append(result, fieldHeader+colorValue+"<invalid>"+colorReset)
			continue
		}

		result = append(result, fieldHeader+fieldLines[0])
		for _, line := range fieldLines[1:] {
			result = append(result, prefix+symbolEmpty+line)
		}
	}
	return result
}

func printMap(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, parentLasts []bool) lines {
	var header string
	if opts.ShowTypes {
		header = fmt.Sprintf("%s%s%s", colorMap, v.Type().String(), colorReset)
	} else {
		header = " ---"
	}

	result := lines{header}
	keys := v.MapKeys()
	sort.Slice(keys, func(i, j int) bool {
		return fmt.Sprintf("%v", keys[i]) < fmt.Sprintf("%v", keys[j])
	})
	keyCount := len(keys)

	for i, key := range keys {
		prefix := buildPrefix(indentLevel, parentLasts)
		isLast := i == keyCount-1
		currentLasts := append(parentLasts, isLast)

		var symbol string
		if isLast {
			symbol = symbolLast
		} else {
			symbol = symbolMid
		}

		keyLines := prettyPrint(key, 0, visited, opts, currentLasts)
		valueLines := prettyPrint(v.MapIndex(key), indentLevel+1, visited, opts, currentLasts)

		keyHeader := fmt.Sprintf("%s%s%s%s: ",
			prefix,
			symbol,
			colorKey,
			strings.Join(keyLines, ""),
		)

		if len(valueLines) == 0 {
			result = append(result, keyHeader+colorValue+"<invalid>"+colorReset)
			continue
		}

		result = append(result, keyHeader+valueLines[0])
		for _, line := range valueLines[1:] {
			result = append(result, prefix+symbolEmpty+line)
		}
	}
	return result
}

func printSlice(v reflect.Value, indentLevel int, visited map[uintptr]bool, opts PrintOptions, parentLasts []bool) lines {
	var header string
	if opts.ShowTypes {
		header = fmt.Sprintf("%s%s%s", colorSlice, v.Type().String(), colorReset)
	} else {
		header = " ---"
	}

	result := lines{header}
	elemCount := v.Len()

	for i := 0; i < elemCount; i++ {
		prefix := buildPrefix(indentLevel, parentLasts)
		isLast := i == elemCount-1
		currentLasts := append(parentLasts, isLast)

		var symbol string
		if isLast {
			symbol = symbolLast
		} else {
			symbol = symbolMid
		}

		elemLines := prettyPrint(v.Index(i), indentLevel+1, visited, opts, currentLasts)
		bullet := fmt.Sprintf("%s%s", prefix, symbol)

		if len(elemLines) == 0 {
			result = append(result, bullet+colorValue+"<invalid>"+colorReset)
			continue
		}

		result = append(result, bullet+elemLines[0])
		for _, line := range elemLines[1:] {
			result = append(result, prefix+symbolEmpty+line)
		}
	}
	return result
}

/*
type Person struct {
	Name    string
	Age     int
	Hobbies []string
	Address struct {
		City  string
		State string
	}
	Metadata map[string]interface{}
}

func main() {
	p := Person{
		Name:    "Alice",
		Age:     30,
		Hobbies: []string{"Reading", "Hiking"},
		Metadata: map[string]interface{}{
			"id":     123,
			"scores": []float64{98.5, 87.2},
			"nested": map[string]interface{}{"a": 1, "b": 2},
		},
	}
	p.Address.City = "New York"
	p.Address.State = "NY"
	p.Metadata["self_ref"] = &p

	fmt.Println("With types:")
	fmt.Println(PrettyPrint(p, PrintOptions{ShowTypes: false}))

	fmt.Println("\nWithout types:\n")
	fmt.Println(PrettyPrint(p))
}
*/
