package apimart

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"

	"juhe-factory/api/internal/provider"
)

type ChatResult struct {
	Content      string
	Raw          json.RawMessage
	FinishReason string
	InputTokens  int64
	OutputTokens int64
	TotalTokens  int64
	UsageRaw     json.RawMessage
}

func (c Client) Chat(ctx context.Context, baseURL, apiKey string, payload map[string]any) (string, json.RawMessage, error) {
	result, err := c.ChatWithUsage(ctx, baseURL, apiKey, payload)
	return result.Content, result.Raw, err
}

func (c Client) ChatWithUsage(ctx context.Context, baseURL, apiKey string, payload map[string]any) (ChatResult, error) {
	var result ChatResult
	requestPayload := make(map[string]any, len(payload)+1)
	for key, value := range payload {
		requestPayload[key] = value
	}
	requestPayload["stream"] = false
	body, err := json.Marshal(requestPayload)
	if err != nil {
		return result, err
	}
	request, err := http.NewRequestWithContext(ctx, http.MethodPost, strings.TrimRight(baseURL, "/")+"/chat/completions", bytes.NewReader(body))
	if err != nil {
		return result, err
	}
	request.Header.Set("Authorization", "Bearer "+apiKey)
	request.Header.Set("Content-Type", "application/json")
	response, err := c.HTTPClient.Do(request)
	if err != nil {
		return result, err
	}
	defer response.Body.Close()
	data, err := io.ReadAll(io.LimitReader(response.Body, 16*1024*1024))
	if err != nil {
		return result, err
	}
	if response.StatusCode < 200 || response.StatusCode >= 300 {
		result.Raw = append(json.RawMessage(nil), data...)
		return result, &provider.HTTPError{StatusCode: response.StatusCode, Body: safeBody(data), RetryAfter: response.Header.Get("Retry-After")}
	}
	type chatChoice struct {
		Message struct {
			Content any `json:"content"`
		} `json:"message"`
		FinishReason string `json:"finish_reason"`
	}
	var decoded struct {
		Choices []chatChoice    `json:"choices"`
		Usage   json.RawMessage `json:"usage"`
		Data    struct {
			Choices []chatChoice    `json:"choices"`
			Usage   json.RawMessage `json:"usage"`
		} `json:"data"`
	}
	if err := json.Unmarshal(data, &decoded); err != nil {
		result.Raw = append(json.RawMessage(nil), data...)
		return result, fmt.Errorf("文本模型响应不是有效 JSON: %w；上游响应: %s", err, safeBody(data))
	}
	result.Raw = append(json.RawMessage(nil), data...)
	result.UsageRaw = decoded.Usage
	choices := decoded.Choices
	if len(choices) == 0 {
		choices = decoded.Data.Choices
		if len(result.UsageRaw) == 0 {
			result.UsageRaw = decoded.Data.Usage
		}
	}
	if len(choices) == 0 {
		return result, fmt.Errorf("文本模型响应缺少 choices；上游响应: %s", safeBody(data))
	}
	result.FinishReason = choices[0].FinishReason
	if len(result.UsageRaw) > 0 {
		var usage map[string]any
		if json.Unmarshal(result.UsageRaw, &usage) == nil {
			result.InputTokens = intValue(usage["prompt_tokens"], usage["input_tokens"])
			result.OutputTokens = intValue(usage["completion_tokens"], usage["output_tokens"])
			result.TotalTokens = intValue(usage["total_tokens"])
			if result.TotalTokens == 0 {
				result.TotalTokens = result.InputTokens + result.OutputTokens
			}
		}
	}
	switch content := choices[0].Message.Content.(type) {
	case string:
		result.Content = content
		return result, nil
	case []any:
		parts := make([]string, 0)
		for _, item := range content {
			if block, ok := item.(map[string]any); ok {
				if text, ok := block["text"].(string); ok {
					parts = append(parts, text)
				}
			}
		}
		result.Content = strings.Join(parts, "\n")
		return result, nil
	default:
		return result, errors.New("文本模型响应内容无效")
	}
}

func intValue(values ...any) int64 {
	for _, value := range values {
		switch number := value.(type) {
		case float64:
			if number > 0 {
				return int64(number)
			}
		case int64:
			if number > 0 {
				return number
			}
		case json.Number:
			if parsed, err := number.Int64(); err == nil && parsed > 0 {
				return parsed
			}
		}
	}
	return 0
}
