package standard

import (
	"fmt"

	agentcontext "github.com/yaoapp/yao/agent/context"
	robottypes "github.com/yaoapp/yao/agent/robot/types"
)

// RunTasks executes P2: Tasks phase
// Calls the Tasks Agent to break down goals into executable tasks
//
// Input:
//   - Goals (from P1) with markdown content
//   - Available resources (Agents, MCP tools, KB, DB)
//
// Output:
//   - List of Task objects with executor assignments, expected outputs, and validation rules
func (e *Executor) RunTasks(ctx *robottypes.Context, exec *robottypes.Execution, _ interface{}) error {
	// §18.2: confirming phase may have already populated Tasks — skip regeneration
	if len(exec.Tasks) > 0 {
		return nil
	}

	// Get robot for resources
	robot := exec.GetRobot()
	if robot == nil {
		return fmt.Errorf("robot not found in execution")
	}

	// Update UI field with i18n
	locale := getEffectiveLocale(robot, exec.Input)
	e.updateUIFields(ctx, exec, "", getLocalizedMessage(locale, "breaking_down_tasks"))

	// Validate: Goals must exist (from P1)
	if exec.Goals == nil || exec.Goals.Content == "" {
		return fmt.Errorf("goals not available for task planning")
	}

	// Get agent ID for tasks phase (per-robot config > global Uses > empty)
	agentID := robottypes.ResolvePhaseAgent(robot.Config, robottypes.PhaseTasks)
	if agentID == "" {
		return fmt.Errorf("no Tasks Agent configured (set uses.tasks in agent.yml or resources.phases in robot config)")
	}

	// Build prompt with goals and available resources
	formatter := NewInputFormatter()
	userContent := formatter.FormatGoals(exec.Goals, robot)

	if userContent == "" {
		return fmt.Errorf("tasks agent (%s) received empty input for task planning", agentID)
	}

	// Call agent
	caller := NewAgentCaller()
	caller.log = newExecLogger(robot, exec.ID)
	caller.Connector = robot.LanguageModel
	caller.Workspace = robot.Workspace
	result, err := caller.CallWithMessages(ctx, agentID, userContent)
	if err != nil {
		return fmt.Errorf("tasks agent (%s) call failed: %w", agentID, err)
	}

	// Parse response as JSON
	// Tasks Agent returns: { "tasks": [...] }
	data, err := result.GetJSON()
	if err != nil {
		return fmt.Errorf("tasks agent (%s) returned invalid JSON: %w", agentID, err)
	}

	// Extract tasks array
	tasksData, ok := data["tasks"].([]interface{})
	if !ok || len(tasksData) == 0 {
		return fmt.Errorf("tasks agent (%s) returned no tasks", agentID)
	}

	// Parse tasks
	tasks, err := ParseTasks(tasksData)
	if err != nil {
		return fmt.Errorf("tasks agent (%s) returned invalid task structure: %w", agentID, err)
	}

	// Validate tasks
	if err := ValidateTasks(tasks); err != nil {
		return fmt.Errorf("tasks validation failed: %w", err)
	}

	exec.Tasks = tasks

	// Log task overview for developer observability
	el := newExecLogger(robot, exec.ID)
	el.logTaskOverview(tasks)

	return nil
}

// ParseTasks converts raw JSON array to []Task
// Tasks are sorted by Order field after parsing
func ParseTasks(data []interface{}) ([]robottypes.Task, error) {
	tasks := make([]robottypes.Task, 0, len(data))

	for i, item := range data {
		taskMap, ok := item.(map[string]interface{})
		if !ok {
			return nil, fmt.Errorf("task %d is not a valid object", i)
		}

		task, err := ParseTask(taskMap, i)
		if err != nil {
			return nil, fmt.Errorf("task %d: %w", i, err)
		}

		tasks = append(tasks, *task)
	}

	// Sort tasks by Order field to ensure correct execution sequence
	SortTasksByOrder(tasks)

	return tasks, nil
}

// ParseTask converts a map to Task struct
func ParseTask(data map[string]interface{}, index int) (*robottypes.Task, error) {
	task := &robottypes.Task{
		Status: robottypes.TaskPending,
		Order:  index,
	}

	// Required: id
	if id, ok := data["id"].(string); ok && id != "" {
		task.ID = id
	} else {
		task.ID = fmt.Sprintf("task-%03d", index+1)
	}

	// Required: executor_type
	if execType, ok := data["executor_type"].(string); ok {
		task.ExecutorType = ParseExecutorType(execType)
	} else {
		return nil, fmt.Errorf("missing executor_type")
	}

	// Required: executor_id
	if execID, ok := data["executor_id"].(string); ok && execID != "" {
		task.ExecutorID = execID
	} else {
		return nil, fmt.Errorf("missing executor_id")
	}

	// Optional: goal_ref
	if goalRef, ok := data["goal_ref"].(string); ok {
		task.GoalRef = goalRef
	}

	// Optional: source (default to auto)
	if source, ok := data["source"].(string); ok {
		task.Source = robottypes.TaskSource(source)
	} else {
		task.Source = robottypes.TaskSourceAuto
	}

	// Optional: order (override default)
	if order, ok := data["order"].(float64); ok {
		task.Order = int(order)
	}

	// Optional: messages (task instructions)
	if messages, ok := data["messages"].([]interface{}); ok {
		task.Messages = ParseMessages(messages)
	}

	// Optional: description - save to Description field and convert to message if no messages
	if desc, ok := data["description"].(string); ok && desc != "" {
		task.Description = desc
		// Also convert to Messages for execution if no explicit messages provided
		if len(task.Messages) == 0 {
			task.Messages = []agentcontext.Message{
				{Role: agentcontext.RoleUser, Content: desc},
			}
		}
	}

	// Optional: args
	if args, ok := data["args"].([]interface{}); ok {
		task.Args = make([]any, len(args))
		copy(task.Args, args)
	}

	// MCP-specific fields (required when executor_type is "mcp")
	if mcpServer, ok := data["mcp_server"].(string); ok {
		task.MCPServer = mcpServer
	}
	if mcpTool, ok := data["mcp_tool"].(string); ok {
		task.MCPTool = mcpTool
	}

	// Optional: expected_output (for P3 validation)
	if expectedOutput, ok := data["expected_output"].(string); ok {
		task.ExpectedOutput = expectedOutput
	}

	// Optional: validation_rules (for P3 validation)
	if rules, ok := data["validation_rules"].([]interface{}); ok {
		task.ValidationRules = make([]string, 0, len(rules))
		for _, r := range rules {
			if s, ok := r.(string); ok {
				task.ValidationRules = append(task.ValidationRules, s)
			}
		}
	}

	return task, nil
}

// ParseMessages converts raw message array to []Message
func ParseMessages(data []interface{}) []agentcontext.Message {
	messages := make([]agentcontext.Message, 0, len(data))

	for _, item := range data {
		msgMap, ok := item.(map[string]interface{})
		if !ok {
			continue
		}

		msg := agentcontext.Message{}

		// Role
		if role, ok := msgMap["role"].(string); ok {
			msg.Role = agentcontext.MessageRole(role)
		} else {
			msg.Role = agentcontext.RoleUser
		}

		// Content
		if content, ok := msgMap["content"].(string); ok {
			msg.Content = content
		} else if content, ok := msgMap["content"]; ok {
			// Handle non-string content (multimodal)
			msg.Content = content
		}

		if msg.Content != nil {
			messages = append(messages, msg)
		}
	}

	return messages
}

// ParseExecutorType converts string to ExecutorType
func ParseExecutorType(s string) robottypes.ExecutorType {
	switch s {
	case "agent", "assistant":
		return robottypes.ExecutorAssistant
	case "mcp":
		return robottypes.ExecutorMCP
	case "process":
		return robottypes.ExecutorProcess
	default:
		return robottypes.ExecutorAssistant // default to assistant
	}
}

// ValidateTasks validates the task list
func ValidateTasks(tasks []robottypes.Task) error {
	if len(tasks) == 0 {
		return fmt.Errorf("no tasks generated")
	}

	seenIDs := make(map[string]bool)

	for i, task := range tasks {
		// Check unique ID
		if seenIDs[task.ID] {
			return fmt.Errorf("task %d: duplicate task ID '%s'", i, task.ID)
		}
		seenIDs[task.ID] = true

		// Check executor
		if task.ExecutorID == "" {
			return fmt.Errorf("task %d (%s): missing executor_id", i, task.ID)
		}

		// Check messages or description
		if len(task.Messages) == 0 {
			return fmt.Errorf("task %d (%s): missing messages or description", i, task.ID)
		}

		// Note: Executor existence is NOT validated here
		// - ValidateExecutorExists() can be called separately if needed
		// - Unknown executors will fail at P3 runtime with clear error message
		// - This allows flexibility for dynamically registered executors

		// Note: Validation rules are optional
		// - P3 can still do basic validation without explicit rules
	}

	return nil
}

// ValidateTasksWithResources validates tasks and checks executor existence
// Returns a list of warnings for unknown executors (does not fail)
func ValidateTasksWithResources(tasks []robottypes.Task, robot *robottypes.Robot) (warnings []string, err error) {
	// First do basic validation
	if err := ValidateTasks(tasks); err != nil {
		return nil, err
	}

	// Then check executor existence (warnings only)
	for _, task := range tasks {
		if !ValidateExecutorExists(task.ExecutorID, task.ExecutorType, robot) {
			warnings = append(warnings, fmt.Sprintf(
				"task %s: executor '%s' (%s) not found in available resources",
				task.ID, task.ExecutorID, task.ExecutorType,
			))
		}
	}

	return warnings, nil
}

// IsValidExecutorType checks if the executor type is valid
func IsValidExecutorType(t robottypes.ExecutorType) bool {
	switch t {
	case robottypes.ExecutorAssistant, robottypes.ExecutorMCP, robottypes.ExecutorProcess:
		return true
	default:
		return false
	}
}

// SortTasksByOrder sorts tasks by their Order field (ascending)
// This ensures tasks are executed in the correct sequence regardless of
// the order they appear in the LLM response
func SortTasksByOrder(tasks []robottypes.Task) {
	for i := 0; i < len(tasks)-1; i++ {
		for j := i + 1; j < len(tasks); j++ {
			if tasks[j].Order < tasks[i].Order {
				tasks[i], tasks[j] = tasks[j], tasks[i]
			}
		}
	}
}

// ValidateExecutorExists checks if the executor ID exists in available resources
// This is an optional validation - tasks with unknown executors will still be created
// but may fail during P3 execution
// For MCP tasks, pass mcpServer as the second parameter (executorID is ignored for MCP)
func ValidateExecutorExists(executorID string, executorType robottypes.ExecutorType, robot *robottypes.Robot) bool {
	if robot == nil || robot.Config == nil || robot.Config.Resources == nil {
		return true // Skip validation if no resources configured
	}

	switch executorType {
	case robottypes.ExecutorAssistant:
		for _, agent := range robot.Config.Resources.Agents {
			if agent == executorID {
				return true
			}
		}
		return false

	case robottypes.ExecutorMCP:
		// For MCP, executorID can be either:
		// 1. The mcp_server value (new format)
		// 2. The combined mcp_server.mcp_tool format (for display)
		// We validate against mcp_server (the MCP server/client ID)
		for _, mcp := range robot.Config.Resources.MCP {
			if mcp.ID == executorID {
				return true
			}
		}
		return false

	case robottypes.ExecutorProcess:
		// Process executors are not validated against resources
		// They are validated at runtime by the Yao process system
		return true
	}

	return false
}

// ValidateMCPTask validates MCP task fields
// Returns an error if mcp_server or mcp_tool is missing for MCP tasks
func ValidateMCPTask(task *robottypes.Task) error {
	if task.ExecutorType != robottypes.ExecutorMCP {
		return nil
	}
	if task.MCPServer == "" {
		return fmt.Errorf("MCP task %s: mcp_server field is required", task.ID)
	}
	if task.MCPTool == "" {
		return fmt.Errorf("MCP task %s: mcp_tool field is required", task.ID)
	}
	return nil
}
