package llm

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

	"github.com/google/generative-ai-go/genai"
	"github.com/openai/openai-go"
	openaioption "github.com/openai/openai-go/option"
	"github.com/volcengine/volcengine-go-sdk/service/arkruntime"
	"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
	"github.com/volcengine/volcengine-go-sdk/volcengine"
	"google.golang.org/api/option"
)

const (
	ProviderOpenAI   = "openai"
	ProviderGemini   = "gemini"
	ProviderDoubao   = "doubao"
	ProviderDeepseek = "deepseek"

	// Model constants
	geminiModel   = "gemini-pro"
	deepseekModel = "deepseek-chat"
	openaiModel   = "chatgpt-4o-latest"
)

const (
	DefaultApiKey   = "YzAzZDIyN2ItMWFkNS00MDNkLWJkM2YtZjgzNzczOWE4YzFj"
	DefaultEndpoint = "ZXAtMjAyNTAxMTMyMzE5NTEtOTJ4bjI="
)

const llmPrompt = `Generate a Git commit message following Conventional Commits v1.0.0: <type>(<scope>): <description>

# Type Selection (by priority):
BREAKING CHANGE: Add ! after type or BREAKING CHANGE: in footer for API changes
fix: Bug fixes, crashes, errors, security issues
feat: New features, APIs, capabilities
perf: Performance improvements
refactor: Code restructuring without functional changes
docs: Documentation only
style: Formatting, whitespace, imports
test: Test changes
build: Build system, dependencies
ci: CI/CD changes
chore: Maintenance, version updates

The commit message should follow these rules:
1. Follow the Conventional Commits format: <type>(<scope>): <description>
2. The body should be one paragraph
3. The body should explain WHAT and WHY (not HOW)
4. Each line should be less than 72 characters
5. There should be a line break between the title and the body
6. Disable markdown formatting

IMPORTANT: Only output the commit message itself, no additional explanation, no template description, no prefatory text. Just the commit message in the format specified.

Here's the diff:`

func GenerateGeminiCommitMessage(diff, apiKey string) (string, error) {
	ctx := context.Background()
	client, err := genai.NewClient(ctx, option.WithAPIKey(apiKey))
	if err != nil {
		return "", fmt.Errorf("creating Gemini client: %w", err)
	}
	defer client.Close()

	model := client.GenerativeModel(geminiModel)
	prompt := fmt.Sprintf("%s\n%s", llmPrompt, diff)

	resp, err := model.GenerateContent(ctx, genai.Text(prompt))
	if err != nil {
		return "", fmt.Errorf("generating commit message: %w", err)
	}

	if len(resp.Candidates) > 0 && len(resp.Candidates[0].Content.Parts) > 0 {
		generatedMessage := resp.Candidates[0].Content.Parts[0].(genai.Text)
		return strings.TrimSpace(string(generatedMessage)), nil
	}

	return "", fmt.Errorf("no commit message generated by Gemini")
}

func GenerateOpenAICommitMessage(diff, apiKey string) (string, error) {
	client := openai.NewClient(
		openaioption.WithAPIKey(apiKey),
	)
	ctx := context.Background()
	prompt := fmt.Sprintf("%s\n%s", llmPrompt, diff)

	chatCompletion, err := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
		Messages: openai.F([]openai.ChatCompletionMessageParamUnion{
			openai.UserMessage(prompt),
		}),
		Model: openai.F(openai.ChatModelGPT4o),
	})
	if err != nil {
		return "", fmt.Errorf("generating commit message: %w", err)
	}

	if len(chatCompletion.Choices) > 0 {
		return strings.TrimSpace(chatCompletion.Choices[0].Message.Content), nil
	}

	return "", fmt.Errorf("no commit message generated by OpenAI")
}

func GenerateDoubaoCommitMessage(diff, apiKey string, endpointId string) (string, error) {
	client := arkruntime.NewClientWithApiKey(
		apiKey,
	)

	ctx := context.Background()

	prompt := fmt.Sprintf("%s\n%s", llmPrompt, diff)

	req := model.ChatCompletionRequest{
		Model: endpointId,
		Messages: []*model.ChatCompletionMessage{
			{
				Role: model.ChatMessageRoleSystem,
				Content: &model.ChatCompletionMessageContent{
					StringValue: volcengine.String("你是豆包，是由字节跳动开发的 AI 人工智能助手，你非常擅长生成 git commit message"),
				},
			},
			{
				Role: model.ChatMessageRoleUser,
				Content: &model.ChatCompletionMessageContent{
					StringValue: volcengine.String(prompt),
				},
			},
		},
	}

	resp, err := client.CreateChatCompletion(ctx, req)
	if err != nil {
		return "", err
	}
	return *resp.Choices[0].Message.Content.StringValue, nil
}

func GenerateDeepseekCommitMessage(diff, apiKey string) (string, error) {
	client := &http.Client{}
	ctx := context.Background()
	prompt := fmt.Sprintf("%s\n%s", llmPrompt, diff)

	reqBody := map[string]interface{}{
		"model": deepseekModel,
		"messages": []map[string]string{
			{
				"role":    "system",
				"content": "You are Deepseek, an AI assistant specialized in generating git commit messages.",
			},
			{
				"role":    "user",
				"content": prompt,
			},
		},
		"stream": false,
	}

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

	req, err := http.NewRequestWithContext(ctx, "POST", "https://api.deepseek.com/chat/completions", bytes.NewBuffer(jsonBody))
	if err != nil {
		return "", fmt.Errorf("creating request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+apiKey)

	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("making request: %w", err)
	}
	defer resp.Body.Close()

	var result map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return "", fmt.Errorf("decoding response: %w", err)
	}

	if choices, ok := result["choices"].([]interface{}); ok && len(choices) > 0 {
		if choice, ok := choices[0].(map[string]interface{}); ok {
			if message, ok := choice["message"].(map[string]interface{}); ok {
				if content, ok := message["content"].(string); ok {
					return content, nil
				}
			}
		}
	}

	return "", fmt.Errorf("invalid response format from Deepseek: %v", result)
}
