package executor

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/everything-on-mysql/cassandra-on-mysql/pkg/cql/parser"
	"github.com/everything-on-mysql/cassandra-on-mysql/pkg/cql/types"
)

type collectionMutationKind uint8

const (
	mutationListAppend collectionMutationKind = iota
	mutationListPrepend
	mutationListIndex
	mutationSetAdd
	mutationSetRemove
	mutationMapMerge
	mutationMapRemove
	mutationMapRemoveKey
	mutationMapSet
)

type collectionMutation struct {
	kind   collectionMutationKind
	target parser.Expression
	value  parser.Expression
}

type assignmentInfo struct {
	assignment *parser.Assignment
	column     string
	cqlType    string
	typeInfo   types.TypeInfo
	mutation   *collectionMutation
}

type collectionKind uint8

const (
	collectionNone collectionKind = iota
	collectionList
	collectionSet
	collectionMap
)

type collectionTypeDetails struct {
	kind      collectionKind
	elemType  string
	elemInfo  types.TypeInfo
	keyType   string
	keyInfo   types.TypeInfo
	valueType string
	valueInfo types.TypeInfo
}

func (e *dmlExecutor) collectAssignmentInfoForUpdate(ctx context.Context, user, password string, stmt *parser.UpdateStatement) ([]assignmentInfo, bool, error) {
	if stmt == nil {
		return nil, false, nil
	}
	columnTypes, err := e.lookupColumnTypes(ctx, user, password, stmt.Table)
	if err != nil {
		return nil, false, err
	}
	typeInfos := make(map[string]types.TypeInfo, len(stmt.Assigments))
	for _, assignment := range stmt.Assigments {
		if assignment == nil {
			continue
		}
		key := strings.ToLower(assignment.Column)
		if _, ok := typeInfos[key]; ok {
			continue
		}
		cqlType := lookupColumnType(columnTypes, assignment.Column)
		info, err := e.resolveTypeInfo(ctx, user, password, cqlType, stmt.Table.Schema)
		if err != nil {
			return nil, false, err
		}
		typeInfos[key] = info
	}
	return collectAssignmentInfo(stmt.Assigments, columnTypes, typeInfos)
}

func collectAssignmentInfo(assignments []*parser.Assignment, columnTypes map[string]string, typeInfos map[string]types.TypeInfo) ([]assignmentInfo, bool, error) {
	out := make([]assignmentInfo, 0, len(assignments))
	hasMutation := false
	hasCounter := false
	hasNonCounter := false
	for _, assignment := range assignments {
		if assignment == nil {
			continue
		}
		column := assignment.Column
		cqlType := normalizeCQLTypeString(lookupColumnType(columnTypes, column))
		info := lookupTypeInfo(typeInfos, column)
		if _, isCounter, err := counterAssignmentDelta(assignment, cqlType); err != nil {
			return nil, false, err
		} else if isCounter {
			if hasNonCounter {
				return nil, false, fmt.Errorf("counter updates cannot mix counter and non-counter columns")
			}
			hasCounter = true
			out = append(out, assignmentInfo{
				assignment: assignment,
				column:     column,
				cqlType:    cqlType,
				typeInfo:   info,
			})
			continue
		}
		if hasCounter {
			return nil, false, fmt.Errorf("counter updates cannot mix counter and non-counter columns")
		}
		hasNonCounter = true
		mutation, err := classifyCollectionMutation(assignment, cqlType)
		if err != nil {
			return nil, false, err
		}
		if mutation != nil {
			hasMutation = true
		} else if err := validateArithmeticAssignment(column, cqlType, assignment.Value); err != nil {
			return nil, false, err
		}
		out = append(out, assignmentInfo{
			assignment: assignment,
			column:     column,
			cqlType:    cqlType,
			typeInfo:   info,
			mutation:   mutation,
		})
	}
	return out, hasMutation, nil
}

func classifyCollectionMutation(assignment *parser.Assignment, columnType string) (*collectionMutation, error) {
	if assignment == nil {
		return nil, nil
	}
	if access, ok := assignment.Target.(*parser.CollectionAccessExpr); ok {
		if access.Slice || access.End != nil {
			return nil, fmt.Errorf("collection slices are not supported")
		}
		if _, ok := columnExprMatch(unwrapTypeHint(access.Target), assignment.Column); !ok {
			return nil, fmt.Errorf("collection access must target the assigned column")
		}
		switch {
		case strings.HasPrefix(columnType, "list<"):
			return &collectionMutation{kind: mutationListIndex, target: access.Start, value: assignment.Value}, nil
		case strings.HasPrefix(columnType, "map<"):
			if isNullLiteral(assignment.Value) {
				return &collectionMutation{kind: mutationMapRemoveKey, target: access.Start}, nil
			}
			return &collectionMutation{kind: mutationMapSet, target: access.Start, value: assignment.Value}, nil
		default:
			return nil, fmt.Errorf("collection element updates require list or map columns")
		}
	}

	bin, ok := assignment.Value.(*parser.BinaryExpr)
	if !ok {
		return nil, nil
	}
	if bin.Op != "+" && bin.Op != "-" {
		return nil, nil
	}

	left := unwrapTypeHint(bin.Left)
	right := unwrapTypeHint(bin.Right)

	if _, ok := columnExprMatch(left, assignment.Column); ok {
		return classifyBinaryMutation(columnType, bin.Op, right, true)
	}
	if _, ok := columnExprMatch(right, assignment.Column); ok {
		return classifyBinaryMutation(columnType, bin.Op, left, false)
	}
	return nil, nil
}

func classifyBinaryMutation(columnType, op string, other parser.Expression, columnOnLeft bool) (*collectionMutation, error) {
	switch {
	case strings.HasPrefix(columnType, "list<"):
		if op != "+" {
			return nil, fmt.Errorf("list mutations only support +")
		}
		if !isListValueExpr(other) {
			return nil, fmt.Errorf("list mutations require list literals or bind markers")
		}
		if columnOnLeft {
			return &collectionMutation{kind: mutationListAppend, value: other}, nil
		}
		return &collectionMutation{kind: mutationListPrepend, value: other}, nil
	case strings.HasPrefix(columnType, "set<"):
		if !isSetValueExpr(other) {
			return nil, fmt.Errorf("set mutations require set literals or bind markers")
		}
		if op == "+" {
			return &collectionMutation{kind: mutationSetAdd, value: other}, nil
		}
		if op == "-" {
			return &collectionMutation{kind: mutationSetRemove, value: other}, nil
		}
		return nil, fmt.Errorf("set mutations only support + or -")
	case strings.HasPrefix(columnType, "map<"):
		if op == "+" {
			if !isMapValueExpr(other) {
				return nil, fmt.Errorf("map merges require map literals or bind markers")
			}
			return &collectionMutation{kind: mutationMapMerge, value: other}, nil
		}
		if op == "-" {
			if !isSetValueExpr(other) {
				return nil, fmt.Errorf("map removals require set literals or bind markers")
			}
			return &collectionMutation{kind: mutationMapRemove, value: other}, nil
		}
		return nil, fmt.Errorf("map mutations only support + or -")
	default:
		return nil, nil
	}
}

func isListValueExpr(expr parser.Expression) bool {
	expr = unwrapTypeHint(expr)
	if _, ok := expr.(*parser.ListLiteral); ok {
		return true
	}
	return isBindMarkerExpr(expr)
}

func isSetValueExpr(expr parser.Expression) bool {
	expr = unwrapTypeHint(expr)
	if _, ok := expr.(*parser.SetLiteral); ok {
		return true
	}
	return isBindMarkerExpr(expr)
}

func isMapValueExpr(expr parser.Expression) bool {
	expr = unwrapTypeHint(expr)
	if _, ok := expr.(*parser.MapLiteral); ok {
		return true
	}
	return isBindMarkerExpr(expr)
}

func isBindMarkerExpr(expr parser.Expression) bool {
	switch expr.(type) {
	case *parser.BindMarker, *parser.NamedBindMarker:
		return true
	default:
		return false
	}
}

func unwrapTypeHint(expr parser.Expression) parser.Expression {
	for {
		hint, ok := expr.(*parser.TypeHintExpr)
		if !ok || hint == nil {
			return expr
		}
		expr = hint.Expr
	}
}

func lookupTypeInfo(typeInfos map[string]types.TypeInfo, column string) types.TypeInfo {
	if typeInfos == nil || column == "" {
		return nil
	}
	if info, ok := typeInfos[strings.ToLower(column)]; ok {
		return info
	}
	return nil
}

func collectionDetails(cqlType string, info types.TypeInfo) (collectionTypeDetails, bool, error) {
	if cqlType == "" {
		return collectionTypeDetails{}, false, nil
	}
	normalized := normalizeCQLTypeString(cqlType)
	if strings.HasPrefix(normalized, "list<") {
		elemType := innerType(normalized)
		var elemInfo types.TypeInfo
		if details, ok := info.(*types.ListTypeInfo); ok {
			if details.ElemType != "" {
				elemType = details.ElemType
			}
			elemInfo = details.ElemInfo
		}
		return collectionTypeDetails{kind: collectionList, elemType: elemType, elemInfo: elemInfo}, true, nil
	}
	if strings.HasPrefix(normalized, "set<") {
		elemType := innerType(normalized)
		var elemInfo types.TypeInfo
		if details, ok := info.(*types.SetTypeInfo); ok {
			if details.ElemType != "" {
				elemType = details.ElemType
			}
			elemInfo = details.ElemInfo
		}
		return collectionTypeDetails{kind: collectionSet, elemType: elemType, elemInfo: elemInfo}, true, nil
	}
	if strings.HasPrefix(normalized, "map<") {
		parts := splitTopLevelTypes(innerType(normalized))
		if len(parts) < 2 {
			return collectionTypeDetails{}, false, fmt.Errorf("invalid map type %q", cqlType)
		}
		keyType := parts[0]
		valueType := parts[1]
		var keyInfo types.TypeInfo
		var valueInfo types.TypeInfo
		if details, ok := info.(*types.MapTypeInfo); ok {
			if details.KeyType != "" {
				keyType = details.KeyType
			}
			if details.ValueType != "" {
				valueType = details.ValueType
			}
			keyInfo = details.KeyInfo
			valueInfo = details.ValueInfo
		}
		return collectionTypeDetails{
			kind:      collectionMap,
			keyType:   keyType,
			keyInfo:   keyInfo,
			valueType: valueType,
			valueInfo: valueInfo,
		}, true, nil
	}
	return collectionTypeDetails{}, false, nil
}

func applyCollectionAssignments(assignments []assignmentInfo, existing map[string]interface{}, params []interface{}, markerIndex map[parser.Expression]int) (map[string]interface{}, error) {
	current := make(map[string]interface{}, len(existing))
	for key, value := range existing {
		current[key] = value
	}
	updates := make(map[string]interface{}, len(assignments))
	for _, info := range assignments {
		details, isCollection, err := collectionDetails(info.cqlType, info.typeInfo)
		if err != nil {
			return nil, err
		}
		if info.mutation != nil {
			newVal, err := applyCollectionMutation(details, current[info.column], info.mutation, params, markerIndex)
			if err != nil {
				return nil, err
			}
			encoded, err := encodeJSONValue(newVal)
			if err != nil {
				return nil, err
			}
			updates[info.column] = encoded
			current[info.column] = newVal
			continue
		}
		value, err := resolveAssignmentValue(info.assignment.Value, details, isCollection, params, markerIndex)
		if err != nil {
			return nil, err
		}
		if isCollection {
			encoded, err := encodeJSONValue(value)
			if err != nil {
				return nil, err
			}
			updates[info.column] = encoded
			current[info.column] = value
		} else {
			updates[info.column] = value
		}
	}
	return updates, nil
}

func resolveAssignmentValue(expr parser.Expression, details collectionTypeDetails, isCollection bool, params []interface{}, markerIndex map[parser.Expression]int) (interface{}, error) {
	if !isCollection {
		return resolveScalarExpr(expr, params, markerIndex)
	}
	if isNullLiteral(expr) {
		return nil, nil
	}
	if value, ok, err := resolveBindMarkerValue(expr, params, markerIndex); err != nil {
		return nil, err
	} else if ok {
		if value == nil {
			return nil, nil
		}
		return decodeCollectionValue(value, details)
	}
	var raw interface{}
	var err error
	switch details.kind {
	case collectionList:
		raw, err = resolveListExpr(expr, details.elemType, details.elemInfo, params, markerIndex)
	case collectionSet:
		raw, err = resolveSetExpr(expr, details.elemType, details.elemInfo, params, markerIndex)
	case collectionMap:
		raw, err = resolveMapExpr(expr, details.keyType, details.keyInfo, details.valueType, details.valueInfo, params, markerIndex)
	default:
		return nil, fmt.Errorf("unsupported collection assignment")
	}
	if err != nil {
		return nil, err
	}
	return raw, nil
}

func applyCollectionMutation(details collectionTypeDetails, existing interface{}, mutation *collectionMutation, params []interface{}, markerIndex map[parser.Expression]int) (interface{}, error) {
	if mutation == nil {
		return existing, nil
	}
	switch mutation.kind {
	case mutationListAppend:
		values, err := resolveListExpr(mutation.value, details.elemType, details.elemInfo, params, markerIndex)
		if err != nil {
			return nil, err
		}
		list, err := ensureListValue(existing)
		if err != nil {
			return nil, err
		}
		return append(list, values...), nil
	case mutationListPrepend:
		values, err := resolveListExpr(mutation.value, details.elemType, details.elemInfo, params, markerIndex)
		if err != nil {
			return nil, err
		}
		list, err := ensureListValue(existing)
		if err != nil {
			return nil, err
		}
		out := make([]interface{}, 0, len(values)+len(list))
		out = append(out, values...)
		out = append(out, list...)
		return out, nil
	case mutationListIndex:
		list, err := ensureListValue(existing)
		if err != nil {
			return nil, err
		}
		index, err := resolveIndexExpr(mutation.target, params, markerIndex)
		if err != nil {
			return nil, err
		}
		if index < 0 || index >= len(list) {
			return nil, fmt.Errorf("list index %d out of range", index)
		}
		value, err := resolveScalarExpr(mutation.value, params, markerIndex)
		if err != nil {
			return nil, err
		}
		jsonVal, err := cqlValueToJSON(value, details.elemType, details.elemInfo)
		if err != nil {
			return nil, err
		}
		list[index] = jsonVal
		return list, nil
	case mutationSetAdd:
		values, err := resolveSetExpr(mutation.value, details.elemType, details.elemInfo, params, markerIndex)
		if err != nil {
			return nil, err
		}
		set, err := ensureListValue(existing)
		if err != nil {
			return nil, err
		}
		return applySetAdd(set, values)
	case mutationSetRemove:
		values, err := resolveSetExpr(mutation.value, details.elemType, details.elemInfo, params, markerIndex)
		if err != nil {
			return nil, err
		}
		set, err := ensureListValue(existing)
		if err != nil {
			return nil, err
		}
		return applySetRemove(set, values)
	case mutationMapMerge:
		incoming, err := resolveMapExpr(mutation.value, details.keyType, details.keyInfo, details.valueType, details.valueInfo, params, markerIndex)
		if err != nil {
			return nil, err
		}
		m, err := ensureMapValue(existing)
		if err != nil {
			return nil, err
		}
		for key, value := range incoming {
			m[key] = value
		}
		return m, nil
	case mutationMapRemove:
		keys, err := resolveMapKeysExpr(mutation.value, details.keyType, details.keyInfo, params, markerIndex)
		if err != nil {
			return nil, err
		}
		m, err := ensureMapValue(existing)
		if err != nil {
			return nil, err
		}
		for key := range keys {
			delete(m, key)
		}
		return m, nil
	case mutationMapSet:
		m, err := ensureMapValue(existing)
		if err != nil {
			return nil, err
		}
		key, err := resolveMapKeyExpr(mutation.target, details.keyType, details.keyInfo, params, markerIndex)
		if err != nil {
			return nil, err
		}
		value, err := resolveScalarExpr(mutation.value, params, markerIndex)
		if err != nil {
			return nil, err
		}
		jsonVal, err := cqlValueToJSON(value, details.valueType, details.valueInfo)
		if err != nil {
			return nil, err
		}
		m[key] = jsonVal
		return m, nil
	case mutationMapRemoveKey:
		m, err := ensureMapValue(existing)
		if err != nil {
			return nil, err
		}
		key, err := resolveMapKeyExpr(mutation.target, details.keyType, details.keyInfo, params, markerIndex)
		if err != nil {
			return nil, err
		}
		delete(m, key)
		return m, nil
	default:
		return nil, fmt.Errorf("unsupported collection mutation")
	}
}

func resolveListExpr(expr parser.Expression, elemType string, elemInfo types.TypeInfo, params []interface{}, markerIndex map[parser.Expression]int) ([]interface{}, error) {
	if value, ok, err := resolveBindMarkerValue(expr, params, markerIndex); err != nil {
		return nil, err
	} else if ok {
		return decodeJSONArray(value)
	}
	literal, ok := unwrapTypeHint(expr).(*parser.ListLiteral)
	if !ok {
		return nil, fmt.Errorf("expected list literal or bind marker")
	}
	values := make([]interface{}, len(literal.Elements))
	for i, elem := range literal.Elements {
		val, err := resolveScalarExpr(elem, params, markerIndex)
		if err != nil {
			return nil, err
		}
		jsonVal, err := cqlValueToJSON(val, elemType, elemInfo)
		if err != nil {
			return nil, err
		}
		values[i] = jsonVal
	}
	return values, nil
}

func resolveSetExpr(expr parser.Expression, elemType string, elemInfo types.TypeInfo, params []interface{}, markerIndex map[parser.Expression]int) ([]interface{}, error) {
	if value, ok, err := resolveBindMarkerValue(expr, params, markerIndex); err != nil {
		return nil, err
	} else if ok {
		return decodeJSONArray(value)
	}
	literal, ok := unwrapTypeHint(expr).(*parser.SetLiteral)
	if !ok {
		return nil, fmt.Errorf("expected set literal or bind marker")
	}
	values := make([]interface{}, len(literal.Elements))
	for i, elem := range literal.Elements {
		val, err := resolveScalarExpr(elem, params, markerIndex)
		if err != nil {
			return nil, err
		}
		jsonVal, err := cqlValueToJSON(val, elemType, elemInfo)
		if err != nil {
			return nil, err
		}
		values[i] = jsonVal
	}
	return values, nil
}

func resolveMapExpr(expr parser.Expression, keyType string, keyInfo types.TypeInfo, valueType string, valueInfo types.TypeInfo, params []interface{}, markerIndex map[parser.Expression]int) (map[string]interface{}, error) {
	if value, ok, err := resolveBindMarkerValue(expr, params, markerIndex); err != nil {
		return nil, err
	} else if ok {
		return decodeJSONMap(value)
	}
	literal, ok := unwrapTypeHint(expr).(*parser.MapLiteral)
	if !ok {
		return nil, fmt.Errorf("expected map literal or bind marker")
	}
	values := make(map[string]interface{}, len(literal.Entries))
	for _, entry := range literal.Entries {
		keyVal, err := resolveScalarExpr(entry.Key, params, markerIndex)
		if err != nil {
			return nil, err
		}
		key, err := cqlValueToString(keyVal, keyType)
		if err != nil {
			return nil, err
		}
		val, err := resolveScalarExpr(entry.Value, params, markerIndex)
		if err != nil {
			return nil, err
		}
		jsonVal, err := cqlValueToJSON(val, valueType, valueInfo)
		if err != nil {
			return nil, err
		}
		values[key] = jsonVal
	}
	return values, nil
}

func resolveMapKeysExpr(expr parser.Expression, keyType string, keyInfo types.TypeInfo, params []interface{}, markerIndex map[parser.Expression]int) (map[string]struct{}, error) {
	if value, ok, err := resolveBindMarkerValue(expr, params, markerIndex); err != nil {
		return nil, err
	} else if ok {
		arr, err := decodeJSONArray(value)
		if err != nil {
			return nil, err
		}
		keys := make(map[string]struct{}, len(arr))
		for _, elem := range arr {
			key, err := cqlValueToString(elem, keyType)
			if err != nil {
				return nil, err
			}
			keys[key] = struct{}{}
		}
		return keys, nil
	}
	literal, ok := unwrapTypeHint(expr).(*parser.SetLiteral)
	if !ok {
		return nil, fmt.Errorf("expected set literal or bind marker")
	}
	keys := make(map[string]struct{}, len(literal.Elements))
	for _, elem := range literal.Elements {
		val, err := resolveScalarExpr(elem, params, markerIndex)
		if err != nil {
			return nil, err
		}
		key, err := cqlValueToString(val, keyType)
		if err != nil {
			return nil, err
		}
		keys[key] = struct{}{}
	}
	return keys, nil
}

func resolveMapKeyExpr(expr parser.Expression, keyType string, keyInfo types.TypeInfo, params []interface{}, markerIndex map[parser.Expression]int) (string, error) {
	val, err := resolveScalarExpr(expr, params, markerIndex)
	if err != nil {
		return "", err
	}
	return cqlValueToString(val, keyType)
}

func resolveIndexExpr(expr parser.Expression, params []interface{}, markerIndex map[parser.Expression]int) (int, error) {
	val, err := resolveScalarExpr(expr, params, markerIndex)
	if err != nil {
		return 0, err
	}
	idx, ok := intFromValue(val)
	if !ok {
		return 0, fmt.Errorf("list index must be an integer")
	}
	return idx, nil
}

func resolveScalarExpr(expr parser.Expression, params []interface{}, markerIndex map[parser.Expression]int) (interface{}, error) {
	if expr == nil {
		return nil, nil
	}
	switch e := expr.(type) {
	case *parser.TypeHintExpr:
		return resolveScalarExpr(e.Expr, params, markerIndex)
	case *parser.BindMarker, *parser.NamedBindMarker:
		if value, _, err := resolveBindMarkerValue(expr, params, markerIndex); err != nil {
			return nil, err
		} else {
			return value, nil
		}
	case *parser.StringLiteral:
		return e.Value, nil
	case *parser.BlobLiteral:
		return e.Value, nil
	case *parser.IntegerLiteral:
		return e.Value, nil
	case *parser.FloatLiteral:
		return e.Value, nil
	case *parser.BooleanLiteral:
		return strings.EqualFold(e.Value, "true"), nil
	case *parser.UUIDLiteral:
		return e.Value, nil
	case *parser.NullLiteral:
		return nil, nil
	case *parser.UnaryExpr:
		val, err := resolveScalarExpr(e.Expr, params, markerIndex)
		if err != nil {
			return nil, err
		}
		return applyUnary(e.Op, val)
	default:
		return nil, fmt.Errorf("unsupported assignment expression")
	}
}

func applyUnary(op string, value interface{}) (interface{}, error) {
	switch op {
	case "+":
		return value, nil
	case "-":
		switch v := value.(type) {
		case int:
			return -v, nil
		case int8:
			return -v, nil
		case int16:
			return -v, nil
		case int32:
			return -v, nil
		case int64:
			return -v, nil
		case float32:
			return -v, nil
		case float64:
			return -v, nil
		default:
			return nil, fmt.Errorf("unsupported unary operand")
		}
	default:
		return nil, fmt.Errorf("unsupported unary operator")
	}
}

func resolveBindMarkerValue(expr parser.Expression, params []interface{}, markerIndex map[parser.Expression]int) (interface{}, bool, error) {
	switch e := expr.(type) {
	case *parser.TypeHintExpr:
		return resolveBindMarkerValue(e.Expr, params, markerIndex)
	case *parser.BindMarker, *parser.NamedBindMarker:
		if len(params) == 0 {
			return nil, true, fmt.Errorf("bind markers require prepared parameters")
		}
		idx, ok := markerIndex[expr]
		if !ok {
			return nil, true, fmt.Errorf("bind marker index not found")
		}
		if idx >= len(params) {
			return nil, true, fmt.Errorf("bind marker index out of range")
		}
		if isUnsetParam(params[idx]) {
			return nil, true, fmt.Errorf("UNSET is not supported in collection mutations")
		}
		return params[idx], true, nil
	default:
		return nil, false, nil
	}
}

func ensureListValue(value interface{}) ([]interface{}, error) {
	if value == nil {
		return []interface{}{}, nil
	}
	list, ok := value.([]interface{})
	if !ok {
		return nil, fmt.Errorf("collection value must be a list")
	}
	return list, nil
}

func ensureMapValue(value interface{}) (map[string]interface{}, error) {
	if value == nil {
		return map[string]interface{}{}, nil
	}
	m, ok := value.(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("collection value must be a map")
	}
	return m, nil
}

func decodeJSONArray(value interface{}) ([]interface{}, error) {
	if value == nil {
		return nil, fmt.Errorf("collection value cannot be null")
	}
	switch v := value.(type) {
	case []byte:
		decoded, err := decodeJSONValue(v)
		if err != nil {
			return nil, err
		}
		if decoded == nil {
			return nil, fmt.Errorf("collection value cannot be null")
		}
		arr, ok := decoded.([]interface{})
		if !ok {
			return nil, fmt.Errorf("collection value must be a JSON array")
		}
		return arr, nil
	case string:
		decoded, err := decodeJSONValue([]byte(v))
		if err != nil {
			return nil, err
		}
		if decoded == nil {
			return nil, fmt.Errorf("collection value cannot be null")
		}
		arr, ok := decoded.([]interface{})
		if !ok {
			return nil, fmt.Errorf("collection value must be a JSON array")
		}
		return arr, nil
	case []interface{}:
		return v, nil
	default:
		return nil, fmt.Errorf("collection value must be a JSON array")
	}
}

func decodeJSONMap(value interface{}) (map[string]interface{}, error) {
	if value == nil {
		return nil, fmt.Errorf("collection value cannot be null")
	}
	switch v := value.(type) {
	case []byte:
		decoded, err := decodeJSONValue(v)
		if err != nil {
			return nil, err
		}
		if decoded == nil {
			return nil, fmt.Errorf("collection value cannot be null")
		}
		m, ok := decoded.(map[string]interface{})
		if !ok {
			return nil, fmt.Errorf("collection value must be a JSON object")
		}
		return m, nil
	case string:
		decoded, err := decodeJSONValue([]byte(v))
		if err != nil {
			return nil, err
		}
		if decoded == nil {
			return nil, fmt.Errorf("collection value cannot be null")
		}
		m, ok := decoded.(map[string]interface{})
		if !ok {
			return nil, fmt.Errorf("collection value must be a JSON object")
		}
		return m, nil
	case map[string]interface{}:
		return v, nil
	default:
		return nil, fmt.Errorf("collection value must be a JSON object")
	}
}

func decodeCollectionValue(value interface{}, details collectionTypeDetails) (interface{}, error) {
	if value == nil {
		return nil, nil
	}
	switch v := value.(type) {
	case []byte:
		decoded, err := decodeJSONValue(v)
		if err != nil {
			return nil, err
		}
		return normalizeDecodedCollection(decoded, details)
	case string:
		decoded, err := decodeJSONValue([]byte(v))
		if err != nil {
			return nil, err
		}
		return normalizeDecodedCollection(decoded, details)
	case []interface{}:
		if details.kind == collectionList || details.kind == collectionSet {
			return v, nil
		}
		return nil, fmt.Errorf("collection value must be a JSON object")
	case map[string]interface{}:
		if details.kind == collectionMap {
			return v, nil
		}
		return nil, fmt.Errorf("collection value must be a JSON array")
	default:
		return nil, fmt.Errorf("collection value must be JSON")
	}
}

func normalizeDecodedCollection(value interface{}, details collectionTypeDetails) (interface{}, error) {
	if value == nil {
		return nil, nil
	}
	switch details.kind {
	case collectionList, collectionSet:
		arr, ok := value.([]interface{})
		if !ok {
			return nil, fmt.Errorf("collection value must be a JSON array")
		}
		return arr, nil
	case collectionMap:
		m, ok := value.(map[string]interface{})
		if !ok {
			return nil, fmt.Errorf("collection value must be a JSON object")
		}
		return m, nil
	default:
		return nil, fmt.Errorf("unsupported collection assignment")
	}
}

func encodeJSONValue(value interface{}) ([]byte, error) {
	if value == nil {
		return nil, nil
	}
	return json.Marshal(value)
}

func applySetAdd(existing []interface{}, values []interface{}) ([]interface{}, error) {
	seen := make(map[string]struct{}, len(existing)+len(values))
	out := make([]interface{}, 0, len(existing)+len(values))
	for _, elem := range existing {
		key, err := setElementKey(elem)
		if err != nil {
			return nil, err
		}
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		out = append(out, elem)
	}
	for _, elem := range values {
		key, err := setElementKey(elem)
		if err != nil {
			return nil, err
		}
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		out = append(out, elem)
	}
	return out, nil
}

func applySetRemove(existing []interface{}, values []interface{}) ([]interface{}, error) {
	remove := make(map[string]struct{}, len(values))
	for _, elem := range values {
		key, err := setElementKey(elem)
		if err != nil {
			return nil, err
		}
		remove[key] = struct{}{}
	}
	out := make([]interface{}, 0, len(existing))
	for _, elem := range existing {
		key, err := setElementKey(elem)
		if err != nil {
			return nil, err
		}
		if _, ok := remove[key]; ok {
			continue
		}
		out = append(out, elem)
	}
	return out, nil
}

func setElementKey(value interface{}) (string, error) {
	encoded, err := json.Marshal(value)
	if err != nil {
		return "", err
	}
	return string(encoded), nil
}

func intFromValue(value interface{}) (int, bool) {
	switch v := value.(type) {
	case int:
		return v, true
	case int8:
		return int(v), true
	case int16:
		return int(v), true
	case int32:
		return int(v), true
	case int64:
		return int(v), true
	case uint:
		return int(v), true
	case uint8:
		return int(v), true
	case uint16:
		return int(v), true
	case uint32:
		return int(v), true
	case uint64:
		return int(v), true
	case json.Number:
		if n, err := v.Int64(); err == nil {
			return int(n), true
		}
	case string:
		if n, err := json.Number(v).Int64(); err == nil {
			return int(n), true
		}
	}
	return 0, false
}
