package parsers

import (
	"encoding/json"

	"github.com/google/go-cmp/cmp"
	"github.com/ollama/ollama/api"
)

// argsComparer provides cmp options for comparing ToolCallFunctionArguments
// It compares by logical equality (same keys with same values) not by order
var argsComparer = cmp.Comparer(func(a, b api.ToolCallFunctionArguments) bool {
	// Convert both to maps and compare
	aMap := a.ToMap()
	bMap := b.ToMap()
	if len(aMap) != len(bMap) {
		return false
	}
	for k, av := range aMap {
		bv, ok := bMap[k]
		if !ok {
			return false
		}
		// Use JSON encoding for deep comparison of values
		aJSON, _ := json.Marshal(av)
		bJSON, _ := json.Marshal(bv)
		if string(aJSON) != string(bJSON) {
			return false
		}
	}
	return true
})

// propsComparer provides cmp options for comparing ToolPropertiesMap
var propsComparer = cmp.Comparer(func(a, b *api.ToolPropertiesMap) bool {
	if a == nil && b == nil {
		return true
	}
	if a == nil || b == nil {
		return false
	}
	aJSON, _ := json.Marshal(a)
	bJSON, _ := json.Marshal(b)
	return string(aJSON) == string(bJSON)
})

// toolsComparer combines argsComparer and propsComparer for comparing tools
var toolsComparer = cmp.Options{argsComparer, propsComparer}

// toolCallEqual compares two tool calls by comparing their components
// It compares arguments by logical equality (same keys with same values) not by order
func toolCallEqual(a, b api.ToolCall) bool {
	if a.ID != b.ID {
		return false
	}
	if a.Function.Index != b.Function.Index {
		return false
	}
	if a.Function.Name != b.Function.Name {
		return false
	}
	// Compare arguments by logical equality using argsComparer logic
	aMap := a.Function.Arguments.ToMap()
	bMap := b.Function.Arguments.ToMap()
	if len(aMap) != len(bMap) {
		return false
	}
	for k, av := range aMap {
		bv, ok := bMap[k]
		if !ok {
			return false
		}
		aJSON, _ := json.Marshal(av)
		bJSON, _ := json.Marshal(bv)
		if string(aJSON) != string(bJSON) {
			return false
		}
	}
	return true
}

// testPropsMap creates a ToolPropertiesMap from a map (convenience function for tests, order not preserved)
func testPropsMap(m map[string]api.ToolProperty) *api.ToolPropertiesMap {
	props := api.NewToolPropertiesMap()
	for k, v := range m {
		props.Set(k, v)
	}
	return props
}

// testArgs creates ToolCallFunctionArguments from a map (convenience function for tests, order not preserved)
func testArgs(m map[string]any) api.ToolCallFunctionArguments {
	args := api.NewToolCallFunctionArguments()
	for k, v := range m {
		args.Set(k, v)
	}
	return args
}

func args(s string) api.ToolCallFunctionArguments {
	var result api.ToolCallFunctionArguments
	if err := json.Unmarshal([]byte(s), &result); err != nil {
		panic("invalid JSON in args(): " + err.Error())
	}
	return result
}
