package ai

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"time"

	clinictracing "github.com/pingcap-inc/clinic/clinic_data_service/pkg/tracing"
)

// OpenAIClient OpenAI API 客户端
type OpenAIClient struct {
	apiKey     string
	baseURL    string
	model      string
	httpClient *http.Client
}

// NewOpenAIClient 创建 OpenAI 客户端
func NewOpenAIClient(apiKey, baseURL, model string) *OpenAIClient {
	if baseURL == "" {
		baseURL = "https://api.openai.com/v1"
	}
	if model == "" {
		model = "gpt-5.4"
	}
	return &OpenAIClient{
		apiKey:  apiKey,
		baseURL: baseURL,
		model:   model,
		httpClient: &http.Client{
			Timeout:   300 * time.Second,
			Transport: clinictracing.NewHTTPTransport(nil),
		},
	}
}

func (c *OpenAIClient) Name() string { return "openai" }

func (c *OpenAIClient) Chat(ctx context.Context, req *ChatRequest) (*ChatResponse, error) {
	// 如果有文件，使用 Responses API
	if len(req.Files) > 0 {
		return c.chatWithFiles(ctx, req)
	}

	// 否则使用传统的 Chat Completions API（保持原有逻辑）
	return c.chatCompletions(ctx, req)
}

func (c *OpenAIClient) chatCompletions(ctx context.Context, req *ChatRequest) (*ChatResponse, error) {
	messages := []openAIMessage{
		{Role: "system", Content: req.SystemPrompt},
		{Role: "user", Content: req.UserMessage},
	}

	body := openAIChatRequest{
		Model:               c.model,
		Messages:            messages,
		MaxCompletionTokens: req.MaxTokens,
		Temperature:         req.Temperature,
	}

	jsonBody, err := json.Marshal(body)
	if err != nil {
		return nil, fmt.Errorf("marshal request: %w", err)
	}

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/chat/completions", bytes.NewReader(jsonBody))
	if err != nil {
		return nil, fmt.Errorf("create request: %w", err)
	}
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+c.apiKey)

	resp, err := c.httpClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("send request: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("OpenAI API error (status %d): %s", resp.StatusCode, string(respBody))
	}

	var chatResp openAIChatResponse
	if err := json.Unmarshal(respBody, &chatResp); err != nil {
		return nil, fmt.Errorf("unmarshal response: %w", err)
	}

	if len(chatResp.Choices) == 0 {
		return nil, fmt.Errorf("OpenAI returned no choices")
	}

	return &ChatResponse{
		Content:      chatResp.Choices[0].Message.Content,
		Model:        chatResp.Model,
		InputTokens:  chatResp.Usage.PromptTokens,
		OutputTokens: chatResp.Usage.CompletionTokens,
	}, nil
}

// OpenAI API 请求/响应结构

type openAIMessage struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

type openAIChatRequest struct {
	Model               string          `json:"model"`
	Messages            []openAIMessage `json:"messages"`
	MaxCompletionTokens int             `json:"max_completion_tokens"`
	Temperature         float64         `json:"temperature"`
}

type openAIChatResponse struct {
	Model   string `json:"model"`
	Choices []struct {
		Message openAIMessage `json:"message"`
	} `json:"choices"`
	Usage struct {
		PromptTokens     int `json:"prompt_tokens"`
		CompletionTokens int `json:"completion_tokens"`
	} `json:"usage"`
}

/* curl https://api.openai.com/v1/responses \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-5.3",
    "input": [
      {
        "role": "user",
        "content": [
          {
            "type": "input_text",
            "text": "Compare multiple inspection reports and identify trends."
          },
          {
            "type": "input_file",
            "file_id": "file-day1"
          },
          {
            "type": "input_file",
            "file_id": "file-day2"
          },
          {
            "type": "input_file",
            "file_id": "file-day3"
          }
        ]
      }
    ]
  }'
 */
// chatWithFiles 使用 Responses API 处理带文件的请求
func (c *OpenAIClient) chatWithFiles(ctx context.Context, req *ChatRequest) (*ChatResponse, error) {
	// 1. 上传所有文件
	fileIDs := make([]string, 0, len(req.Files))
	defer func() {
		// 清理上传的文件
		for _, fileID := range fileIDs {
			_ = c.deleteFile(context.Background(), fileID)
		}
	}()

	for _, file := range req.Files {
		fileID, err := c.uploadFile(ctx, file)
		if err != nil {
			return nil, fmt.Errorf("upload file %s: %w", file.Name, err)
		}
		fileIDs = append(fileIDs, fileID)
	}

	// 2. 构造 Responses API 请求
	// 构造 content 数组：先添加文本指令，再添加文件
	content := make([]map[string]any, 0, len(fileIDs)+1)

	// 添加文本指令（合并 SystemPrompt 和 UserMessage）
	inputText := req.SystemPrompt
	if req.UserMessage != "" {
		inputText += "\n\n" + req.UserMessage
	}
	content = append(content, map[string]any{
		"type": "input_text",
		"text": inputText,
	})

	// 添加所有文件
	for _, fileID := range fileIDs {
		content = append(content, map[string]any{
			"type":    "input_file",
			"file_id": fileID,
		})
	}

	// 构造 input 数组（包含一个 user 消息）
	input := []map[string]any{
		{
			"role":    "user",
			"content": content,
		},
	}

	body := map[string]any{
		"model":       c.model,
		"input":       input,
		"temperature": req.Temperature,
	}

	// Responses API 使用 max_output_tokens 而不是 max_completion_tokens
	if req.MaxTokens > 0 {
		body["max_output_tokens"] = req.MaxTokens
	}

	jsonBody, err := json.Marshal(body)
	if err != nil {
		return nil, fmt.Errorf("marshal request: %w", err)
	}

	// 3. 发送请求到 Responses API
	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost,
		c.baseURL+"/responses", bytes.NewReader(jsonBody))
	if err != nil {
		return nil, fmt.Errorf("create request: %w", err)
	}
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Authorization", "Bearer "+c.apiKey)

	resp, err := c.httpClient.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("send request: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Responses API error (status %d): %s",
			resp.StatusCode, string(respBody))
	}

	// 4. 解析响应
	var responsesResp struct {
		Model  string `json:"model"`
		Output []struct {
			Role    string `json:"role"`
			Content []struct {
				Type string `json:"type"`
				Text string `json:"text"`
			} `json:"content"`
		} `json:"output"`
		Usage struct {
			InputTokens  int `json:"input_tokens"`
			OutputTokens int `json:"output_tokens"`
		} `json:"usage"`
	}
	if err := json.Unmarshal(respBody, &responsesResp); err != nil {
		return nil, fmt.Errorf("unmarshal response: %w", err)
	}

	// 提取所有 output_text 内容并合并
	var outputText string
	for _, msg := range responsesResp.Output {
		for _, content := range msg.Content {
			if content.Type == "output_text" {
				outputText += content.Text
			}
		}
	}

	return &ChatResponse{
		Content:      outputText,
		Model:        responsesResp.Model,
		InputTokens:  responsesResp.Usage.InputTokens,
		OutputTokens: responsesResp.Usage.OutputTokens,
	}, nil
}

// uploadFile 上传文件到 OpenAI Files API
func (c *OpenAIClient) uploadFile(ctx context.Context, file FileInput) (string, error) {
	var buf bytes.Buffer
	writer := multipart.NewWriter(&buf)

	_ = writer.WriteField("purpose", "user_data")

	part, err := writer.CreateFormFile("file", file.Name)
	if err != nil {
		return "", fmt.Errorf("create form file: %w", err)
	}
	if _, err = part.Write(file.Content); err != nil {
		return "", fmt.Errorf("write file content: %w", err)
	}
	writer.Close()

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost,
		c.baseURL+"/files", &buf)
	if err != nil {
		return "", fmt.Errorf("create request: %w", err)
	}
	httpReq.Header.Set("Content-Type", writer.FormDataContentType())
	httpReq.Header.Set("Authorization", "Bearer "+c.apiKey)

	resp, err := c.httpClient.Do(httpReq)
	if err != nil {
		return "", fmt.Errorf("send request: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("upload failed (status %d): %s",
			resp.StatusCode, string(respBody))
	}

	var fileResp struct {
		ID string `json:"id"`
	}
	if err := json.Unmarshal(respBody, &fileResp); err != nil {
		return "", fmt.Errorf("unmarshal response: %w", err)
	}

	return fileResp.ID, nil
}

// deleteFile 删除上传的文件（清理资源）
func (c *OpenAIClient) deleteFile(ctx context.Context, fileID string) error {
	httpReq, err := http.NewRequestWithContext(ctx, http.MethodDelete,
		c.baseURL+"/files/"+fileID, nil)
	if err != nil {
		return err
	}
	httpReq.Header.Set("Authorization", "Bearer "+c.apiKey)

	resp, err := c.httpClient.Do(httpReq)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("delete failed (status %d): %s",
			resp.StatusCode, string(body))
	}

	return nil
}
