package model

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

// StreamingResponseLogger captures the final streaming response for logging.
// It must capture data before forwarding since the caller may mutate the response.
type StreamingResponseLogger struct {
	finishReason string
	content      string
	reasoning    string
	toolCalls    []ResponseToolCall
}

// Capture captures data from a streaming response. Call this for each response
// before forwarding it. It only captures from the final response (when FinishReason is set).
func (l *StreamingResponseLogger) Capture(resp ChatResponse) {
	if len(resp.Choices) == 0 {
		return
	}

	fr := resp.Choices[0].FinishReason()
	if fr == "" {
		return
	}

	l.finishReason = fr
	if msg := resp.Choices[0].Message; msg != nil {
		l.content = msg.Content
		l.reasoning = msg.Reasoning
		l.toolCalls = append([]ResponseToolCall(nil), msg.ToolCalls...)
	}
}

// String returns a formatted string for logging.
func (l *StreamingResponseLogger) String() string {
	var b strings.Builder
	b.WriteString("\n")

	fmt.Fprintf(&b, "FinishReason: %s\n", l.finishReason)
	fmt.Fprintf(&b, "Role: assistant\n")

	if l.content != "" {
		fmt.Fprintf(&b, "Content (400 bytes): %.400s\n", l.content)
	}

	if l.reasoning != "" {
		fmt.Fprintf(&b, "Reasoning (400 bytes): %.400s\n", l.reasoning)
	}

	if len(l.toolCalls) > 0 {
		fmt.Fprintf(&b, "ToolCalls len=%d\n", len(l.toolCalls))
		for j, tc := range l.toolCalls {
			args, _ := json.Marshal(map[string]any(tc.Function.Arguments))
			fmt.Fprintf(&b, "  tc[%d]: id=%s funcName=%s args=%s\n", j, tc.ID, tc.Function.Name, args)
		}
	}

	return b.String()
}

// fmtBytes formats a byte count as a human-readable string (e.g. "6.79 GB").
func fmtBytes(n uint64) string {
	const (
		kb = 1024
		mb = 1024 * kb
		gb = 1024 * mb
	)

	switch {
	case n >= gb:
		return fmt.Sprintf("%.2f GB", float64(n)/float64(gb))
	case n >= mb:
		return fmt.Sprintf("%.2f MB", float64(n)/float64(mb))
	case n >= kb:
		return fmt.Sprintf("%.2f KB", float64(n)/float64(kb))
	default:
		return fmt.Sprintf("%d B", n)
	}
}

// fmtDur formats a duration as whole milliseconds (e.g. "103ms").
func fmtDur(d time.Duration) string {
	return fmt.Sprintf("%dms", d.Milliseconds())
}
