package tools

import (
	"context"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/base64"
	"fmt"
	"hash"
	"net/url"
)

// CryptoTool provides cryptographic and encoding utilities for the LLM.
// Supports hashing, base64, and URL encoding.
type CryptoTool struct{}

// NewCryptoTool creates a crypto/encoding tool.
func NewCryptoTool() *CryptoTool {
	return &CryptoTool{}
}

// Definition returns the tool definition for LLM function calling.
func (t *CryptoTool) Definition() ToolDefinition {
	return ToolDefinition{
		Name:        "crypto",
		Description: "加密哈希与编码工具。计算MD5/SHA哈希值，执行Base64编码/解码，URL编码/解码。",
		Parameters: map[string]interface{}{
			"type": "object",
			"properties": map[string]interface{}{
				"action": map[string]interface{}{
					"type":        "string",
					"enum":        []string{"hash", "base64_encode", "base64_decode", "url_encode", "url_decode"},
					"description": "操作类型。hash: 计算哈希值；base64_encode: Base64编码；base64_decode: Base64解码；url_encode: URL编码；url_decode: URL解码",
				},
				"input": map[string]interface{}{
					"type":        "string",
					"description": "输入数据，需要处理的字符串",
				},
				"algorithm": map[string]interface{}{
					"type":        "string",
					"enum":        []string{"md5", "sha1", "sha256", "sha512"},
					"description": "哈希算法（用于 hash 操作），默认 sha256",
				},
			},
			"required": []string{"action", "input"},
		},
	}
}

// Execute performs crypto/encoding operations.
func (t *CryptoTool) Execute(ctx context.Context, arguments map[string]interface{}) (*ToolResult, error) {
	action, ok := arguments["action"].(string)
	if !ok || action == "" {
		return &ToolResult{
			ToolName: "crypto",
			Success:  false,
			Error:    "缺少 action 参数",
		}, nil
	}

	input, ok := arguments["input"].(string)
	if !ok {
		return &ToolResult{
			ToolName: "crypto",
			Success:  false,
			Error:    "缺少 input 参数",
		}, nil
	}

	switch action {
	case "hash":
		return t.handleHash(arguments)
	case "base64_encode":
		return t.handleBase64Encode(input)
	case "base64_decode":
		return t.handleBase64Decode(input)
	case "url_encode":
		return t.handleURLEncode(input)
	case "url_decode":
		return t.handleURLDecode(input)
	default:
		return &ToolResult{
			ToolName: "crypto",
			Success:  false,
			Error:    fmt.Sprintf("未知操作: %s，支持: hash, base64_encode, base64_decode, url_encode, url_decode", action),
		}, nil
	}
}

// handleHash computes a hash of the input using the specified algorithm.
func (t *CryptoTool) handleHash(arguments map[string]interface{}) (*ToolResult, error) {
	input, _ := arguments["input"].(string)
	algorithm, _ := arguments["algorithm"].(string)
	if algorithm == "" {
		algorithm = "sha256"
	}

	var h hash.Hash
	switch algorithm {
	case "md5":
		h = md5.New()
	case "sha1":
		h = sha1.New()
	case "sha256":
		h = sha256.New()
	case "sha512":
		h = sha512.New()
	default:
		return &ToolResult{
			ToolName: "crypto",
			Success:  false,
			Error:    fmt.Sprintf("不支持的哈希算法: %s，支持: md5, sha1, sha256, sha512", algorithm),
		}, nil
	}

	h.Write([]byte(input))
	hashBytes := h.Sum(nil)
	hashHex := fmt.Sprintf("%x", hashBytes)

	return &ToolResult{
		ToolName: "crypto",
		Success:  true,
		Data: fmt.Sprintf("哈希算法: %s\n输入长度: %d 字节\n哈希值 (hex): %s\n哈希长度: %d 位",
			algorithm, len(input), hashHex, len(hashBytes)*8),
	}, nil
}

// handleBase64Encode encodes input to Base64.
func (t *CryptoTool) handleBase64Encode(input string) (*ToolResult, error) {
	encoded := base64.StdEncoding.EncodeToString([]byte(input))

	return &ToolResult{
		ToolName: "crypto",
		Success:  true,
		Data: fmt.Sprintf("Base64 编码结果:\n原始 (%d 字节): %s\n编码 (%d 字符): %s",
			len(input), truncate(input, 100), len(encoded), encoded),
	}, nil
}

// handleBase64Decode decodes a Base64 string.
func (t *CryptoTool) handleBase64Decode(input string) (*ToolResult, error) {
	// Try standard encoding first, then URL-safe
	decoded, err := base64.StdEncoding.DecodeString(input)
	if err != nil {
		decoded, err = base64.RawStdEncoding.DecodeString(input)
		if err != nil {
			decoded, err = base64.URLEncoding.DecodeString(input)
			if err != nil {
				decoded, err = base64.RawURLEncoding.DecodeString(input)
				if err != nil {
					return &ToolResult{
						ToolName: "crypto",
						Success:  false,
						Error:    fmt.Sprintf("Base64 解码失败: 输入不是有效的 Base64 字符串"),
					}, nil
				}
			}
		}
	}

	return &ToolResult{
		ToolName: "crypto",
		Success:  true,
		Data: fmt.Sprintf("Base64 解码结果:\n原始 (%d 字符): %s\n解码 (%d 字节): %s",
			len(input), truncate(input, 100), len(decoded), truncate(string(decoded), 200)),
	}, nil
}

// handleURLEncode URL-encodes the input string.
func (t *CryptoTool) handleURLEncode(input string) (*ToolResult, error) {
	encoded := url.QueryEscape(input)

	return &ToolResult{
		ToolName: "crypto",
		Success:  true,
		Data: fmt.Sprintf("URL 编码结果:\n原始 (%d 字节): %s\n编码 (%d 字节): %s",
			len(input), truncate(input, 100), len(encoded), encoded),
	}, nil
}

// handleURLDecode URL-decodes the input string.
func (t *CryptoTool) handleURLDecode(input string) (*ToolResult, error) {
	decoded, err := url.QueryUnescape(input)
	if err != nil {
		return &ToolResult{
			ToolName: "crypto",
			Success:  false,
			Error:    fmt.Sprintf("URL 解码失败: %v", err),
		}, nil
	}

	return &ToolResult{
		ToolName: "crypto",
		Success:  true,
		Data: fmt.Sprintf("URL 解码结果:\n原始 (%d 字节): %s\n解码 (%d 字节): %s",
			len(input), truncate(input, 100), len(decoded), truncate(decoded, 200)),
	}, nil
}

// truncate truncates a string to maxLen characters, adding "..." if truncated.
func truncate(s string, maxLen int) string {
	runes := []rune(s)
	if len(runes) <= maxLen {
		return s
	}
	return string(runes[:maxLen]) + "..."
}
