package tools

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

// SpawnTool spawns subagent clones to handle tasks in the background.
//
// Routing:
//   - mode="async" (default): return immediately, subagent announces result when done
//   - mode="sync": block until done, return result inline
type SpawnTool struct {
	subagentMgr *SubagentManager
	parentID    string
	depth       int
}

func NewSpawnTool(manager *SubagentManager, parentID string, depth int) *SpawnTool {
	return &SpawnTool{
		subagentMgr: manager,
		parentID:    parentID,
		depth:       depth,
	}
}

func (t *SpawnTool) Name() string { return "spawn" }

func (t *SpawnTool) Description() string {
	return "Spawn a subagent to handle a task in the background. The subagent runs independently and reports back when done."
}

func (t *SpawnTool) Parameters() map[string]any {
	return map[string]any{
		"type": "object",
		"properties": map[string]any{
			"action": map[string]any{
				"type":        "string",
				"description": "'spawn' (default), 'list', 'cancel', or 'steer'",
			},
			"task": map[string]any{
				"type":        "string",
				"description": "The task to complete (required for action=spawn)",
			},
			"mode": map[string]any{
				"type":        "string",
				"description": "'async' (default, returns immediately) or 'sync' (blocks until done)",
			},
			"label": map[string]any{
				"type":        "string",
				"description": "Short label for the task (for display)",
			},
			"model": map[string]any{
				"type":        "string",
				"description": "Optional model override (e.g. 'anthropic/claude-sonnet-4-5-20250929')",
			},
			"id": map[string]any{
				"type":        "string",
				"description": "Task ID for cancel/steer. For cancel: use 'all' to cancel all or 'last' for most recent",
			},
			"message": map[string]any{
				"type":        "string",
				"description": "New instructions (required for action=steer)",
			},
		},
		"required": []string{"task"},
	}
}

func (t *SpawnTool) Execute(ctx context.Context, args map[string]any) *Result {
	action, _ := args["action"].(string)
	if action == "" {
		action = "spawn"
	}

	switch action {
	case "list":
		return t.executeList(ctx)
	case "cancel":
		return t.executeCancel(ctx, args)
	case "steer":
		return t.executeSteer(ctx, args)
	default:
		return t.executeSpawn(ctx, args)
	}
}

func (t *SpawnTool) executeSpawn(ctx context.Context, args map[string]any) *Result {
	// Reject legacy "agent" parameter — delegation was removed.
	// Guide the LLM to use team_tasks for team coordination.
	if agentKey, _ := args["agent"].(string); agentKey != "" {
		return ErrorResult(fmt.Sprintf(
			"spawn does not accept 'agent' parameter. spawn is for self-clone subagent only. "+
				"To delegate work to team member %q, use: team_tasks(action=\"create\", subject=\"...\", description=\"...\", assignee=%q)",
			agentKey, agentKey))
	}

	task, _ := args["task"].(string)
	if task == "" {
		return ErrorResult("task parameter is required")
	}

	mode, _ := args["mode"].(string)
	if mode == "sync" {
		return t.executeSubagentSync(ctx, args, task)
	}
	return t.executeSubagentAsync(ctx, args, task)
}

// executeSubagentAsync spawns an async self-clone.
func (t *SpawnTool) executeSubagentAsync(ctx context.Context, args map[string]any, task string) *Result {
	label, _ := args["label"].(string)
	modelOverride, _ := args["model"].(string)

	channel := ToolChannelFromCtx(ctx)
	chatID := ToolChatIDFromCtx(ctx)
	peerKind := ToolPeerKindFromCtx(ctx)
	callback := ToolAsyncCBFromCtx(ctx)

	parentID := ToolAgentKeyFromCtx(ctx)
	if parentID == "" {
		parentID = t.parentID
	}

	msg, err := t.subagentMgr.Spawn(ctx, parentID, t.depth, task, label, modelOverride,
		channel, chatID, peerKind, callback)
	if err != nil {
		return ErrorResult(err.Error())
	}

	forLLM := fmt.Sprintf(`{"status":"accepted","label":%q}
%s
After all spawn tool calls in this turn are complete, briefly tell the user what tasks you've started. Subagents will announce results when done — do NOT wait or poll.`, label, msg)

	return AsyncResult(forLLM)
}

// executeSubagentSync runs a sync self-clone.
func (t *SpawnTool) executeSubagentSync(ctx context.Context, args map[string]any, task string) *Result {
	label, _ := args["label"].(string)
	if label == "" {
		label = truncate(task, 50)
	}

	channel := ToolChannelFromCtx(ctx)
	chatID := ToolChatIDFromCtx(ctx)

	parentID := ToolAgentKeyFromCtx(ctx)
	if parentID == "" {
		parentID = t.parentID
	}

	result, iterations, err := t.subagentMgr.RunSync(ctx, parentID, t.depth, task, label,
		channel, chatID)
	if err != nil {
		return ErrorResult(fmt.Sprintf("Subagent '%s' failed: %v", label, err))
	}

	forUser := fmt.Sprintf("Subagent '%s' completed.", label)
	if len(result) > 500 {
		forUser += "\n" + result[:500] + "..."
	} else {
		forUser += "\n" + result
	}

	forLLM := fmt.Sprintf("Subagent '%s' completed in %d iterations.\n\nFull result:\n%s",
		label, iterations, result)

	return &Result{ForLLM: forLLM, ForUser: forUser}
}

// executeList shows active subagent tasks.
func (t *SpawnTool) executeList(ctx context.Context) *Result {
	parentID := ToolAgentKeyFromCtx(ctx)
	if parentID == "" {
		parentID = t.parentID
	}
	tasks := t.subagentMgr.ListTasks(parentID)
	if len(tasks) == 0 {
		return &Result{ForLLM: "No active tasks found."}
	}

	var lines []string
	running, completed, cancelled := 0, 0, 0
	for _, task := range tasks {
		switch task.Status {
		case "running":
			running++
		case "completed":
			completed++
		case "cancelled":
			cancelled++
		}
		line := fmt.Sprintf("- [%s] %s (id=%s, status=%s)", task.Label, truncate(task.Task, 60), task.ID, task.Status)
		if task.CompletedAt > 0 {
			dur := time.Duration(task.CompletedAt-task.CreatedAt) * time.Millisecond
			line += fmt.Sprintf(", took %s", dur.Round(time.Millisecond))
		}
		lines = append(lines, line)
	}

	return &Result{ForLLM: fmt.Sprintf("Subagent tasks: %d running, %d completed, %d cancelled\n%s",
		running, completed, cancelled, strings.Join(lines, "\n"))}
}

// executeCancel cancels a subagent task by ID.
func (t *SpawnTool) executeCancel(ctx context.Context, args map[string]any) *Result {
	id, _ := args["id"].(string)
	if id == "" {
		return ErrorResult("id is required for action=cancel")
	}

	if t.subagentMgr.CancelTask(id) {
		return &Result{ForLLM: fmt.Sprintf("Task '%s' cancelled.", id)}
	}

	return ErrorResult(fmt.Sprintf("Task '%s' not found or not running.", id))
}

// executeSteer redirects a running subagent with new instructions.
func (t *SpawnTool) executeSteer(ctx context.Context, args map[string]any) *Result {
	id, _ := args["id"].(string)
	if id == "" {
		return ErrorResult("id is required for action=steer")
	}
	message, _ := args["message"].(string)
	if message == "" {
		return ErrorResult("message is required for action=steer")
	}

	msg, err := t.subagentMgr.Steer(ctx, id, message, nil)
	if err != nil {
		return ErrorResult(err.Error())
	}
	return &Result{ForLLM: msg}
}

// SetContext is a no-op; channel/chatID are now read from ctx (thread-safe).
func (t *SpawnTool) SetContext(channel, chatID string) {}

// SetPeerKind is a no-op; peerKind is now read from ctx (thread-safe).
func (t *SpawnTool) SetPeerKind(peerKind string) {}

// SetCallback is a no-op; callback is now read from ctx (thread-safe).
func (t *SpawnTool) SetCallback(cb AsyncCallback) {}

// --- Helpers moved from old subagent_tool.go ---

// FilterDenyList returns tool names from the registry excluding denied tools.
func FilterDenyList(reg *Registry, denyList []string) []string {
	deny := make(map[string]bool, len(denyList))
	for _, n := range denyList {
		deny[n] = true
	}

	var allowed []string
	for _, name := range reg.List() {
		if !deny[name] {
			allowed = append(allowed, name)
		}
	}
	return allowed
}

// IsSubagentDenied checks if a tool name is in the subagent deny list.
func IsSubagentDenied(toolName string, depth, maxDepth int) bool {
	for _, d := range SubagentDenyAlways {
		if strings.EqualFold(toolName, d) {
			return true
		}
	}
	if depth >= maxDepth {
		for _, d := range SubagentDenyLeaf {
			if strings.EqualFold(toolName, d) {
				return true
			}
		}
	}
	return false
}
