package tools

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"
	"unicode"
)

// DateTimeTool provides date/time operations for the LLM.
// Supports current time, formatting, date arithmetic, and timezone listing.
type DateTimeTool struct{}

// NewDateTimeTool creates a date/time tool.
func NewDateTimeTool() *DateTimeTool {
	return &DateTimeTool{}
}

// Definition returns the tool definition for LLM function calling.
func (t *DateTimeTool) Definition() ToolDefinition {
	return ToolDefinition{
		Name:        "datetime",
		Description: "日期时间工具。获取当前时间、格式化日期、日期加减、计算日期差、查看可用时区。",
		Parameters: map[string]interface{}{
			"type": "object",
			"properties": map[string]interface{}{
				"action": map[string]interface{}{
					"type":        "string",
					"enum":        []string{"now", "format", "add", "diff", "timezone_list"},
					"description": "操作类型。now: 获取当前时间；format: 格式化日期；add: 日期加减；diff: 计算两个日期的差值；timezone_list: 列出常用时区",
				},
				"format": map[string]interface{}{
					"type":        "string",
					"description": "日期格式串（Go风格）。默认 \"2006-01-02 15:04:05\"。常用: \"2006-01-02\"（仅日期）、\"15:04:05\"（仅时间）",
				},
				"timezone": map[string]interface{}{
					"type":        "string",
					"description": "时区标识，如 \"Asia/Shanghai\"、\"America/New_York\"、\"UTC\"。默认使用服务器本地时区",
				},
				"date": map[string]interface{}{
					"type":        "string",
					"description": "基准日期，格式为 \"2006-01-02 15:04:05\" 或 \"2006-01-02\"",
				},
				"duration": map[string]interface{}{
					"type":        "string",
					"description": "时长字符串，如 \"24h\"、\"7d\"、\"30m\"、\"1h30m\"。支持单位: s(秒), m(分钟), h(小时), d(天), w(周), M(月), y(年)",
				},
				"date2": map[string]interface{}{
					"type":        "string",
					"description": "第二个日期（用于 diff 操作），格式同 date",
				},
			},
			"required": []string{"action"},
		},
	}
}

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

	switch action {
	case "now":
		return t.handleNow(arguments)
	case "format":
		return t.handleFormat(arguments)
	case "add":
		return t.handleAdd(arguments)
	case "diff":
		return t.handleDiff(arguments)
	case "timezone_list":
		return t.handleTimezoneList()
	default:
		return &ToolResult{
			ToolName: "datetime",
			Success:  false,
			Error:    fmt.Sprintf("未知操作: %s，支持: now, format, add, diff, timezone_list", action),
		}, nil
	}
}

// handleNow returns the current date/time in the specified timezone.
func (t *DateTimeTool) handleNow(arguments map[string]interface{}) (*ToolResult, error) {
	tz, err := t.getTimezone(arguments)
	if err != nil {
		return &ToolResult{
			ToolName: "datetime",
			Success:  false,
			Error:    err.Error(),
		}, nil
	}

	format := t.getFormat(arguments)
	now := time.Now().In(tz)

	return &ToolResult{
		ToolName: "datetime",
		Success:  true,
		Data: fmt.Sprintf("当前时间: %s\n时区: %s\nUnix时间戳: %d",
			now.Format(format), tz.String(), now.Unix()),
	}, nil
}

// handleFormat formats a given date string.
func (t *DateTimeTool) handleFormat(arguments map[string]interface{}) (*ToolResult, error) {
	dateStr, _ := arguments["date"].(string)
	if dateStr == "" {
		return &ToolResult{
			ToolName: "datetime",
			Success:  false,
			Error:    "format 操作需要 date 参数",
		}, nil
	}

	parsed, err := t.parseDate(dateStr)
	if err != nil {
		return &ToolResult{
			ToolName: "datetime",
			Success:  false,
			Error:    fmt.Sprintf("日期解析失败: %v", err),
		}, nil
	}

	tz, err := t.getTimezone(arguments)
	if err != nil {
		return &ToolResult{
			ToolName: "datetime",
			Success:  false,
			Error:    err.Error(),
		}, nil
	}

	format := t.getFormat(arguments)
	formatted := parsed.In(tz).Format(format)

	return &ToolResult{
		ToolName: "datetime",
		Success:  true,
		Data:     fmt.Sprintf("原始: %s\n格式化: %s\n时区: %s", dateStr, formatted, tz.String()),
	}, nil
}

// handleAdd adds/subtracts a duration from a date.
func (t *DateTimeTool) handleAdd(arguments map[string]interface{}) (*ToolResult, error) {
	durationStr, _ := arguments["duration"].(string)
	if durationStr == "" {
		return &ToolResult{
			ToolName: "datetime",
			Success:  false,
			Error:    "add 操作需要 duration 参数",
		}, nil
	}

	dateStr, _ := arguments["date"].(string)
	var base time.Time
	if dateStr != "" {
		var err error
		base, err = t.parseDate(dateStr)
		if err != nil {
			return &ToolResult{
				ToolName: "datetime",
				Success:  false,
				Error:    fmt.Sprintf("日期解析失败: %v", err),
			}, nil
		}
	} else {
		tz, _ := t.getTimezone(arguments)
		base = time.Now().In(tz)
	}

	dur, err := t.parseDuration(durationStr)
	if err != nil {
		return &ToolResult{
			ToolName: "datetime",
			Success:  false,
			Error:    fmt.Sprintf("时长解析失败: %v", err),
		}, nil
	}

	tz, _ := t.getTimezone(arguments)

	result := base.In(tz)

	// Extract months and years from the duration string (not handled by time.Duration)
	months := extractDurationUnit(durationStr, 'M')
	years := extractDurationUnit(durationStr, 'y')

	if months != 0 || years != 0 {
		result = result.AddDate(years, months, 0)
	}

	// Add the standard duration part
	if dur != 0 {
		result = result.Add(dur)
	}

	format := t.getFormat(arguments)
	return &ToolResult{
		ToolName: "datetime",
		Success:  true,
		Data: fmt.Sprintf("基准日期: %s\n操作: %s\n结果: %s",
			base.In(tz).Format(format), durationStr, result.Format(format)),
	}, nil
}

// handleDiff calculates the difference between two dates.
func (t *DateTimeTool) handleDiff(arguments map[string]interface{}) (*ToolResult, error) {
	dateStr, _ := arguments["date"].(string)
	date2Str, _ := arguments["date2"].(string)

	if dateStr == "" || date2Str == "" {
		return &ToolResult{
			ToolName: "datetime",
			Success:  false,
			Error:    "diff 操作需要 date 和 date2 参数",
		}, nil
	}

	d1, err := t.parseDate(dateStr)
	if err != nil {
		return &ToolResult{
			ToolName: "datetime",
			Success:  false,
			Error:    fmt.Sprintf("date 解析失败: %v", err),
		}, nil
	}

	d2, err := t.parseDate(date2Str)
	if err != nil {
		return &ToolResult{
			ToolName: "datetime",
			Success:  false,
			Error:    fmt.Sprintf("date2 解析失败: %v", err),
		}, nil
	}

	diff := d2.Sub(d1)
	absDiff := diff
	if absDiff < 0 {
		absDiff = -absDiff
	}

	days := int(absDiff.Hours() / 24)
	hours := int(absDiff.Hours()) % 24
	minutes := int(absDiff.Minutes()) % 60
	seconds := int(absDiff.Seconds()) % 60

	sign := ""
	if diff < 0 {
		sign = "-"
	}

	return &ToolResult{
		ToolName: "datetime",
		Success:  true,
		Data: fmt.Sprintf("日期1: %s\n日期2: %s\n差值: %s%d天 %d小时 %d分钟 %d秒 (总计 %s%.0f秒)",
			dateStr, date2Str, sign, days, hours, minutes, seconds, sign, absDiff.Seconds()),
	}, nil
}

// handleTimezoneList returns a list of common timezones.
func (t *DateTimeTool) handleTimezoneList() (*ToolResult, error) {
	zones := []string{
		"UTC",
		"Asia/Shanghai (北京时间)",
		"Asia/Tokyo (东京时间)",
		"Asia/Seoul (首尔时间)",
		"Asia/Singapore (新加坡时间)",
		"Asia/Kolkata (印度时间)",
		"Asia/Dubai (迪拜时间)",
		"Europe/London (伦敦时间)",
		"Europe/Paris (巴黎时间)",
		"Europe/Berlin (柏林时间)",
		"Europe/Moscow (莫斯科时间)",
		"America/New_York (纽约时间)",
		"America/Chicago (芝加哥时间)",
		"America/Denver (丹佛时间)",
		"America/Los_Angeles (洛杉矶时间)",
		"America/Sao_Paulo (圣保罗时间)",
		"Australia/Sydney (悉尼时间)",
		"Pacific/Auckland (奥克兰时间)",
	}

	var result strings.Builder
	result.WriteString("常用时区列表:\n\n")
	for i, z := range zones {
		result.WriteString(fmt.Sprintf("  %2d. %s\n", i+1, z))
	}

	return &ToolResult{
		ToolName: "datetime",
		Success:  true,
		Data:     result.String(),
	}, nil
}

// getTimezone extracts the timezone from arguments, defaulting to Asia/Shanghai.
func (t *DateTimeTool) getTimezone(arguments map[string]interface{}) (*time.Location, error) {
	tzName, _ := arguments["timezone"].(string)
	if tzName == "" {
		loc, err := time.LoadLocation("Asia/Shanghai")
		if err != nil {
			return time.Local, nil
		}
		return loc, nil
	}
	loc, err := time.LoadLocation(tzName)
	if err != nil {
		return nil, fmt.Errorf("无效时区: %s", tzName)
	}
	return loc, nil
}

// getFormat extracts the format string from arguments, defaulting to standard format.
func (t *DateTimeTool) getFormat(arguments map[string]interface{}) string {
	format, _ := arguments["format"].(string)
	if format == "" {
		return "2006-01-02 15:04:05"
	}
	return format
}

// parseDate parses a date string with multiple format attempts.
func (t *DateTimeTool) parseDate(s string) (time.Time, error) {
	formats := []string{
		"2006-01-02 15:04:05",
		"2006-01-02T15:04:05Z",
		"2006-01-02T15:04:05",
		"2006-01-02",
		"2006/01/02 15:04:05",
		"2006/01/02",
		time.RFC3339,
		time.RFC3339Nano,
	}
	for _, f := range formats {
		if t, err := time.Parse(f, s); err == nil {
			return t, nil
		}
	}
	return time.Time{}, fmt.Errorf("无法解析日期: %s", s)
}

// parseDuration parses a human-friendly duration string like "24h", "7d", "1h30m".
func (t *DateTimeTool) parseDuration(s string) (time.Duration, error) {
	// First try standard Go duration parsing
	if d, err := time.ParseDuration(s); err == nil {
		return d, nil
	}

	// Custom parsing for days and weeks
	var total time.Duration
	remaining := s

	for len(remaining) > 0 {
		// find the number
		numStart := 0
		for numStart < len(remaining) && !unicode.IsDigit(rune(remaining[numStart])) && remaining[numStart] != '-' {
			numStart++
		}
		if numStart >= len(remaining) {
			break
		}

		numEnd := numStart
		for numEnd < len(remaining) && (unicode.IsDigit(rune(remaining[numEnd])) || remaining[numEnd] == '.') {
			numEnd++
		}

		val, err := strconv.ParseFloat(remaining[numStart:numEnd], 64)
		if err != nil {
			return 0, fmt.Errorf("无效时长数字: %s", remaining[numStart:numEnd])
		}

		unitEnd := numEnd
		for unitEnd < len(remaining) && unicode.IsLetter(rune(remaining[unitEnd])) {
			unitEnd++
		}
		unit := remaining[numEnd:unitEnd]

		switch unit {
		case "s":
			total += time.Duration(val * float64(time.Second))
		case "m":
			total += time.Duration(val * float64(time.Minute))
		case "h":
			total += time.Duration(val * float64(time.Hour))
		case "d":
			total += time.Duration(val * 24 * float64(time.Hour))
		case "w":
			total += time.Duration(val * 7 * 24 * float64(time.Hour))
		default:
			// skip unknown units (M and y handled elsewhere)
		}

		remaining = remaining[unitEnd:]
	}

	return total, nil
}

// extractDurationUnit extracts numeric value for a given unit character from a duration string.
// e.g., extractDurationUnit("3M", 'M') returns 3, extractDurationUnit("1y2M", 'y') returns 1.
func extractDurationUnit(s string, unit byte) int {
	for i := 0; i < len(s); i++ {
		if s[i] == unit {
			// Scan backwards to find the start of the number
			j := i - 1
			for j >= 0 && (unicode.IsDigit(rune(s[j])) || s[j] == '.') {
				j--
			}
			numStr := s[j+1 : i]
			val, err := strconv.Atoi(numStr)
			if err != nil {
				return 0
			}
			return val
		}
	}
	return 0
}

