package llm

import (
	"context"
	"fmt"
	"log/slog"
	"runtime"
	"strings"
	"sync"
)

// callTools executes each tool call in the message concurrently and returns
// the resulting tool result messages in the same order as the input calls.
func (c *Client) callTools(ctx context.Context, msg Message) []Message {
	var (
		results = make([]Message, len(msg.ToolCalls))
		sem     = make(chan struct{}, runtime.NumCPU()*2)
		wg      sync.WaitGroup
	)
	for i, tc := range msg.ToolCalls {
		wg.Add(1)
		go func(idx int, tc ToolCall) {
			defer wg.Done()
			sem <- struct{}{}
			defer func() { <-sem }()

			c.log.InfoContext(
				ctx,
				"calling tool",
				slog.String("name", tc.Function.Name),
				slog.String("args", tc.Function.Arguments),
			)

			result, err := c.registry.Execute(
				ctx, tc.Function.Name, tc.Function.Arguments,
			)
			if err != nil {
				c.log.ErrorContext(
					ctx,
					"failed to call tool",
					slog.Any("error", err),
					slog.String("name", tc.Function.Name),
				)
				result = fmt.Sprintf(
					`{"ok": false, "error": %q}`, err,
				)
			} else {
				c.log.InfoContext(
					ctx,
					"called tool",
					slog.String("name", tc.Function.Name),
				)
			}

			// Content cannot be empty.
			if strings.TrimSpace(result) == "" {
				result = `{"ok": true, "result": null}`
			}

			results[idx] = Message{
				Role: RoleTool,
				ContentParts: []ContentPart{{
					Type: ContentTypeText, Text: result,
				}},
				Name:       tc.Function.Name,
				ToolCallID: tc.ID,
			}
		}(i, tc)
	}

	wg.Wait()
	return results
}
