// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.

// list-type.go
package kern

import (
	"fmt"
	"reflect"
	"strings"
)

type ListType []any

func IsList(v any) (ok bool) {
	_, ok = v.(*ListType)
	return ok
}

func NewListA(listAny ...any) (list *ListType) {
	if listAny == nil {
		listAny = []any{}
	}
	return NewList(listAny)
}

func NewList(listAny []any) (list *ListType) {
	if listAny != nil {
		ls := make(ListType, len(listAny))
		copy(ls, listAny)
		list = &ls
	}
	return
}

func MakeList(length, capacity int) (list *ListType) {
	if capacity < length {
		capacity = length
	}
	ls := make(ListType, length, capacity)
	list = &ls
	return
}

func ListFromStrings(stringList []string) (list *ListType) {
	list = MakeList(len(stringList), 0)
	for i, s := range stringList {
		(*list)[i] = s
	}
	return
}

func (dict *ListType) ToString(opt FmtOpt) (s string) {
	indent := GetFormatIndent(opt)
	flags := GetFormatFlags(opt)

	var sb strings.Builder
	sb.WriteByte('[')
	if len(*dict) > 0 {
		innerOpt := MakeFormatOptions(flags, indent+1)
		nest := strings.Repeat("  ", indent+1)

		if flags&MultiLine != 0 {
			sb.WriteByte('\n')
			sb.WriteString(nest)
		}
		for i, item := range []any(*dict) {
			if i > 0 {
				if flags&MultiLine != 0 {
					sb.WriteString(",\n")
					sb.WriteString(nest)
				} else {
					sb.WriteString(", ")
				}
			}
			if s, ok := item.(string); ok {
				sb.WriteByte('"')
				sb.WriteString(s)
				sb.WriteByte('"')
			} else if formatter, ok := item.(Formatter); ok {
				sb.WriteString(formatter.ToString(innerOpt))
			} else {
				sb.WriteString(fmt.Sprintf("%v", item))
			}
		}
		if flags&MultiLine != 0 {
			sb.WriteByte('\n')
			sb.WriteString(strings.Repeat("  ", indent))
		}
	}
	sb.WriteByte(']')
	s = sb.String()
	if flags&Truncate != 0 && len(s) > TruncateSize {
		s = TruncateString(s)
	}
	return
}

func (dict *ListType) String() string {
	return dict.ToString(0)
}

func (dict *ListType) TypeName() string {
	return "list"
}

func (dict *ListType) Contains(t *ListType) (answer bool) {
	if len(*dict) >= len(*t) {
		answer = true
		for _, item := range *t {
			if answer = dict.IndexDeepSameCmp(item) >= 0; !answer {
				break
			}
		}
	}
	return
}

func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
	if ls2 != nil && len(*ls1) == len(ls2) {
		answer = true
		for index, i1 := range *ls1 {
			// if !reflect.DeepEqual(i1, ls2[index]) {
			// 	answer = false
			// 	break
			// }
			if !Equal(i1, ls2[index]) {
				answer = false
				break
			}
		}
	}
	return
}

func (dict *ListType) IndexDeepSameCmp(target any) (index int) {
	var eq bool
	var err error
	index = -1
	for i, item := range *dict {
		if eq, err = deepSame(item, target, SameContent); err != nil {
			break
		} else if eq {
			index = i
			break
		}
	}
	return
}

func SameContent(a, b any) (same bool, err error) {
	la, _ := a.(*ListType)
	lb, _ := b.(*ListType)
	if len(*la) == len(*lb) {
		same = true
		for _, item := range *la {
			if pos := lb.IndexDeepSameCmp(item); pos < 0 {
				same = false
				break
			}
		}
	}
	return
}

func deepSame(a, b any, deepCmp DeepFuncTemplate) (eq bool, err error) {
	if IsNumOrFract(a) && IsNumOrFract(b) {
		if IsNumber(a) && IsNumber(b) {
			if IsInteger(a) && IsInteger(b) {
				li, _ := a.(int64)
				ri, _ := b.(int64)
				eq = li == ri
			} else {
				eq = NumAsFloat(a) == NumAsFloat(b)
			}
		} else {
			var cmp int
			if cmp, err = CmpAnyFract(a, b); err == nil {
				eq = cmp == 0
			}
		}
	} else if deepCmp != nil && IsList(a) && IsList(b) {
		eq, err = deepCmp(a, b)
	} else {
		eq = reflect.DeepEqual(a, b)
	}

	return
}

func (dict *ListType) SetItem(index int64, value any) (err error) {
	if index >= 0 && index < int64(len(*dict)) {
		(*dict)[index] = value
	} else {
		err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*dict)-1)
	}
	return
}

func (dict *ListType) AppendItem(value any) {
	*dict = append(*dict, value)
}
