package openai

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

type ChatCompletionRequest struct {
	Model       string          `json:"model"`
	Messages    []interface{}   `json:"messages"`
	Stream      bool            `json:"stream"`
	Tools       []interface{}   `json:"tools"`
	ToolChoice  interface{}     `json:"tool_choice"`
	Functions   []interface{}   `json:"functions"`
	FunctionCall interface{}    `json:"function_call"`
}

func NormalizeModelID(raw string) string {
	trimmed := strings.TrimSpace(raw)
	if trimmed == "" {
		return ""
	}
	parts := strings.Split(trimmed, "/")
	last := parts[len(parts)-1]
	if last == "" {
		return ""
	}
	return last
}

func imageURLToText(imageURL interface{}) string {
	if imageURL == nil {
		return "[Image]"
	}
	var url string
	switch v := imageURL.(type) {
	case string:
		url = v
	case map[string]interface{}:
		if u, ok := v["url"].(string); ok {
			url = u
		}
	}
	if url == "" {
		return "[Image]"
	}
	if strings.HasPrefix(url, "data:") {
		end := strings.Index(url, ";")
		mime := "image"
		if end > 5 {
			mime = url[5:end]
		}
		return "[Image: base64 " + mime + "]"
	}
	return "[Image: " + url + "]"
}

func MessageContentToText(content interface{}) string {
	if content == nil {
		return ""
	}
	switch v := content.(type) {
	case string:
		return v
	case []interface{}:
		var parts []string
		for _, p := range v {
			if p == nil {
				continue
			}
			switch part := p.(type) {
			case string:
				parts = append(parts, part)
			case map[string]interface{}:
				typ, _ := part["type"].(string)
				switch typ {
				case "text":
					if t, ok := part["text"].(string); ok {
						parts = append(parts, t)
					}
				case "image_url":
					parts = append(parts, imageURLToText(part["image_url"]))
				case "image":
					src := part["source"]
					if src == nil {
						src = part["url"]
					}
					parts = append(parts, imageURLToText(src))
				}
			}
		}
		return strings.Join(parts, " ")
	}
	return ""
}

func ToolsToSystemText(tools []interface{}, functions []interface{}) string {
	var defs []interface{}

	for _, t := range tools {
		if m, ok := t.(map[string]interface{}); ok {
			if m["type"] == "function" {
				if fn := m["function"]; fn != nil {
					defs = append(defs, fn)
				}
			} else {
				defs = append(defs, t)
			}
		}
	}
	defs = append(defs, functions...)

	if len(defs) == 0 {
		return ""
	}

	var lines []string
	lines = append(lines, "Available tools (respond with a JSON object to call one):", "")

	for _, raw := range defs {
		fn, ok := raw.(map[string]interface{})
		if !ok {
			continue
		}
		name, _ := fn["name"].(string)
		desc, _ := fn["description"].(string)
		params := "{}"
		if p := fn["parameters"]; p != nil {
			if b, err := json.MarshalIndent(p, "", "  "); err == nil {
				params = string(b)
			}
		} else if p := fn["input_schema"]; p != nil {
			if b, err := json.MarshalIndent(p, "", "  "); err == nil {
				params = string(b)
			}
		}
		lines = append(lines, "Function: "+name+"\nDescription: "+desc+"\nParameters: "+params)
	}

	lines = append(lines, "",
		"When you want to call a tool, use this EXACT format:",
		"",
		"<tool_call>",
		`{"name": "function_name", "arguments": {"param1": "value1"}}`,
		"</tool_call>",
		"",
		"Rules:",
		"- Write your reasoning BEFORE the tool call",
		"- You may make multiple tool calls by using multiple <tool_call> blocks",
		"- STOP writing after the last </tool_call> tag",
		"- If no tool is needed, respond normally without <tool_call> tags",
	)

	return strings.Join(lines, "\n")
}

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

func BuildPromptFromMessages(messages []interface{}) string {
	var systemParts []string
	var convo []string

	for _, raw := range messages {
		m, ok := raw.(map[string]interface{})
		if !ok {
			continue
		}
		role, _ := m["role"].(string)
		text := MessageContentToText(m["content"])

		switch role {
		case "system", "developer":
			if text != "" {
				systemParts = append(systemParts, text)
			}
		case "user":
			if text != "" {
				convo = append(convo, "User: "+text)
			}
		case "assistant":
			toolCalls, _ := m["tool_calls"].([]interface{})
			if len(toolCalls) > 0 {
				var parts []string
				if text != "" {
					parts = append(parts, text)
				}
				for _, tc := range toolCalls {
					tcMap, ok := tc.(map[string]interface{})
					if !ok {
						continue
					}
					fn, _ := tcMap["function"].(map[string]interface{})
					if fn == nil {
						continue
					}
					name, _ := fn["name"].(string)
					args, _ := fn["arguments"].(string)
					if args == "" {
						args = "{}"
					}
					parts = append(parts, fmt.Sprintf("<tool_call>\n{\"name\": \"%s\", \"arguments\": %s}\n</tool_call>", name, args))
				}
				if len(parts) > 0 {
					convo = append(convo, "Assistant: "+strings.Join(parts, "\n"))
				}
			} else if text != "" {
				convo = append(convo, "Assistant: "+text)
			}
		case "tool", "function":
			name, _ := m["name"].(string)
			toolCallID, _ := m["tool_call_id"].(string)
			label := "Tool result"
			if name != "" {
				label = "Tool result (" + name + ")"
			}
			if toolCallID != "" {
				label += " [id=" + toolCallID + "]"
			}
			if text != "" {
				convo = append(convo, label+": "+text)
			}
		}
	}

	system := ""
	if len(systemParts) > 0 {
		system = "System:\n" + strings.Join(systemParts, "\n\n") + "\n\n"
	}
	transcript := strings.Join(convo, "\n\n")
	return system + transcript + "\n\nAssistant:"
}

func BuildPromptFromSimpleMessages(messages []SimpleMessage) string {
	ifaces := make([]interface{}, len(messages))
	for i, m := range messages {
		ifaces[i] = map[string]interface{}{"role": m.Role, "content": m.Content}
	}
	return BuildPromptFromMessages(ifaces)
}
