package tools

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/YspCoder/clawgo/pkg/providers"
)

type SessionInfo struct {
	Key             string
	Kind            string
	Summary         string
	CompactionCount int
	UpdatedAt       time.Time
}

type SessionsTool struct {
	listFn    func(limit int) []SessionInfo
	historyFn func(key string, limit int) []providers.Message
}

func NewSessionsTool(listFn func(limit int) []SessionInfo, historyFn func(key string, limit int) []providers.Message) *SessionsTool {
	return &SessionsTool{listFn: listFn, historyFn: historyFn}
}

func (t *SessionsTool) Name() string { return "sessions" }

func (t *SessionsTool) Description() string {
	return "Inspect sessions in current runtime: list or history"
}

func (t *SessionsTool) Parameters() map[string]interface{} {
	return map[string]interface{}{
		"type": "object",
		"properties": map[string]interface{}{
			"action":         map[string]interface{}{"type": "string", "description": "list|history"},
			"key":            map[string]interface{}{"type": "string", "description": "session key for history"},
			"limit":          map[string]interface{}{"type": "integer", "description": "max items", "default": 20},
			"active_minutes": map[string]interface{}{"type": "integer", "description": "only sessions updated in recent N minutes (list action)"},
			"kinds":          map[string]interface{}{"type": "array", "items": map[string]interface{}{"type": "string"}, "description": "optional session kinds filter for list"},
			"query":          map[string]interface{}{"type": "string", "description": "optional text query for list or history"},
			"include_tools":  map[string]interface{}{"type": "boolean", "description": "include tool role messages in history", "default": false},
			"from_me":        map[string]interface{}{"type": "boolean", "description": "history only: filter assistant messages when true, user messages when false"},
			"role":           map[string]interface{}{"type": "string", "description": "history only: filter by role, e.g. user|assistant|tool|system"},
			"around":         map[string]interface{}{"type": "integer", "description": "1-indexed message index center for history window"},
			"before":         map[string]interface{}{"type": "integer", "description": "1-indexed message index upper bound (exclusive)"},
			"after":          map[string]interface{}{"type": "integer", "description": "1-indexed message index lower bound (exclusive)"},
		},
		"required": []string{"action"},
	}
}

func (t *SessionsTool) Execute(ctx context.Context, args map[string]interface{}) (string, error) {
	_ = ctx
	action := strings.ToLower(MapStringArg(args, "action"))
	limit := MapIntArg(args, "limit", 20)
	includeTools, _ := MapBoolArg(args, "include_tools")
	around := MapIntArg(args, "around", 0)
	before := MapIntArg(args, "before", 0)
	after := MapIntArg(args, "after", 0)
	activeMinutes := MapIntArg(args, "active_minutes", 0)
	query := strings.ToLower(MapStringArg(args, "query"))
	roleFilter := strings.ToLower(MapStringArg(args, "role"))
	fromMe, fromMeSet := MapBoolArg(args, "from_me")
	kindFilter := map[string]struct{}{}
	for _, s := range MapStringListArg(args, "kinds") {
		s = strings.ToLower(strings.TrimSpace(s))
		if s != "" {
			kindFilter[s] = struct{}{}
		}
	}
	type sessionActionHandler func() (string, error)
	handlers := map[string]sessionActionHandler{
		"list": func() (string, error) {
			if t.listFn == nil {
				return "sessions list unavailable", nil
			}
			items := t.listFn(limit * 3)
			if len(items) == 0 {
				return "No sessions.", nil
			}
			if len(kindFilter) > 0 {
				filtered := make([]SessionInfo, 0, len(items))
				for _, s := range items {
					k := strings.ToLower(strings.TrimSpace(s.Kind))
					if _, ok := kindFilter[k]; ok {
						filtered = append(filtered, s)
					}
				}
				items = filtered
			}
			if activeMinutes > 0 {
				cutoff := time.Now().Add(-time.Duration(activeMinutes) * time.Minute)
				filtered := make([]SessionInfo, 0, len(items))
				for _, s := range items {
					if s.UpdatedAt.After(cutoff) {
						filtered = append(filtered, s)
					}
				}
				items = filtered
			}
			if query != "" {
				filtered := make([]SessionInfo, 0, len(items))
				for _, s := range items {
					blob := strings.ToLower(s.Key + "\n" + s.Kind + "\n" + s.Summary)
					if strings.Contains(blob, query) {
						filtered = append(filtered, s)
					}
				}
				items = filtered
			}
			if len(items) == 0 {
				return "No sessions (after filters).", nil
			}
			sort.Slice(items, func(i, j int) bool { return items[i].UpdatedAt.After(items[j].UpdatedAt) })
			if len(items) > limit {
				items = items[:limit]
			}
			var sb strings.Builder
			sb.WriteString("Sessions:\n")
			for _, s := range items {
				sb.WriteString(fmt.Sprintf("- %s kind=%s compactions=%d updated=%s\n", s.Key, s.Kind, s.CompactionCount, s.UpdatedAt.Format(time.RFC3339)))
			}
			return sb.String(), nil
		},
		"history": func() (string, error) {
			if t.historyFn == nil {
				return "sessions history unavailable", nil
			}
			key := MapStringArg(args, "key")
			if key == "" {
				return "key is required for history", nil
			}
			raw := t.historyFn(key, 0)
			if len(raw) == 0 {
				return "No history.", nil
			}
			type indexedMsg struct {
				idx int
				msg providers.Message
			}
			window := make([]indexedMsg, 0, len(raw))
			for i, m := range raw {
				window = append(window, indexedMsg{idx: i + 1, msg: m})
			}

			// Window selectors are 1-indexed (human-friendly)
			if around > 0 {
				center := around - 1
				if center < 0 {
					center = 0
				}
				if center >= len(window) {
					center = len(window) - 1
				}
				half := limit / 2
				if half < 1 {
					half = 1
				}
				start := center - half
				if start < 0 {
					start = 0
				}
				end := center + half + 1
				if end > len(window) {
					end = len(window)
				}
				window = window[start:end]
			} else {
				start := 0
				end := len(window)
				if after > 0 {
					start = after
					if start > len(window) {
						start = len(window)
					}
				}
				if before > 0 {
					end = before - 1
					if end < 0 {
						end = 0
					}
					if end > len(window) {
						end = len(window)
					}
				}
				if start > end {
					start = end
				}
				window = window[start:end]
			}

			if !includeTools {
				filtered := make([]indexedMsg, 0, len(window))
				for _, m := range window {
					if strings.ToLower(m.msg.Role) == "tool" {
						continue
					}
					filtered = append(filtered, m)
				}
				window = filtered
			}
			if roleFilter != "" {
				filtered := make([]indexedMsg, 0, len(window))
				for _, m := range window {
					if strings.ToLower(m.msg.Role) == roleFilter {
						filtered = append(filtered, m)
					}
				}
				window = filtered
			}
			if fromMeSet {
				targetRole := "user"
				if fromMe {
					targetRole = "assistant"
				}
				filtered := make([]indexedMsg, 0, len(window))
				for _, m := range window {
					if strings.ToLower(m.msg.Role) == targetRole {
						filtered = append(filtered, m)
					}
				}
				window = filtered
			}
			if query != "" {
				filtered := make([]indexedMsg, 0, len(window))
				for _, m := range window {
					blob := strings.ToLower(m.msg.Role + "\n" + m.msg.Content)
					if strings.Contains(blob, query) {
						filtered = append(filtered, m)
					}
				}
				window = filtered
			}
			if len(window) == 0 {
				return "No history (after filters).", nil
			}
			if len(window) > limit {
				window = window[len(window)-limit:]
			}
			var sb strings.Builder
			sb.WriteString(fmt.Sprintf("History for %s:\n", key))
			for _, item := range window {
				content := item.msg.Content
				if len(content) > 180 {
					content = content[:180] + "..."
				}
				sb.WriteString(fmt.Sprintf("- [#%d][%s] %s\n", item.idx, item.msg.Role, content))
			}
			return sb.String(), nil
		},
	}
	if handler := handlers[action]; handler != nil {
		return handler()
	}
	return "unsupported action", nil
}
