package standard

import (
	"fmt"
	"strings"

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

// RunGoals executes P1: Goals phase
// Calls the Goals Agent to plan daily objectives
//
// Input:
//   - InspirationReport (from P0) for clock trigger
//   - TriggerInput for human/event trigger
//
// Output:
//   - Goals with markdown content and delivery info
func (e *Executor) RunGoals(ctx *robottypes.Context, exec *robottypes.Execution, _ interface{}) error {
	// §18.2: confirming phase may have already populated Goals — skip regeneration
	if exec.Goals != nil && exec.Goals.Content != "" {
		return nil
	}

	// Get robot for identity and 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, "planning_goals"))

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

	// Build prompt based on trigger type
	formatter := NewInputFormatter()
	var userContent string

	switch exec.TriggerType {
	case robottypes.TriggerClock:
		// For clock trigger: use InspirationReport from P0
		if exec.Inspiration != nil {
			userContent = formatter.FormatInspirationReport(exec.Inspiration)
		} else {
			// Fallback: if no inspiration report, create minimal context
			userContent = formatter.FormatClockContext(
				robottypes.NewClockContext(exec.StartTime, ""),
				robot,
			)
		}

	case robottypes.TriggerHuman, robottypes.TriggerEvent:
		// For human/event trigger: use TriggerInput directly
		if exec.Input != nil {
			userContent = formatter.FormatTriggerInput(exec.Input)
		}
	}

	// Add robot identity context if not already included
	// For clock trigger with inspiration report, identity is not in the report
	// For human/event trigger, identity provides context
	if robot.Config != nil && robot.Config.Identity != nil {
		if !strings.Contains(userContent, "## Robot Identity") {
			userContent = formatter.FormatRobotIdentity(robot) + "\n\n" + userContent
		}
	}

	// Add available resources - critical for generating achievable goals
	// Without knowing what tools are available, goals might be unachievable
	resourcesContent := formatter.FormatAvailableResources(robot)
	if resourcesContent != "" {
		userContent += "\n\n" + resourcesContent
	}

	if userContent == "" {
		return fmt.Errorf("no input available for goals generation")
	}

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

	// Parse response as JSON
	// Goals Agent returns: { "content": "...", "delivery": {...} }
	data, err := result.GetJSON()
	if err != nil {
		// Fallback: if not JSON, use raw text as content
		content := result.GetText()
		if content == "" {
			return fmt.Errorf("goals agent returned empty response")
		}
		exec.Goals = &robottypes.Goals{
			Content: content,
		}
		return nil
	}

	// Build Goals from JSON
	exec.Goals = &robottypes.Goals{}

	// Extract content (markdown)
	if content, ok := data["content"].(string); ok {
		exec.Goals.Content = content
	}

	// Extract delivery
	if delivery, ok := data["delivery"].(map[string]interface{}); ok {
		exec.Goals.Delivery = ParseDelivery(delivery)
	}

	// Validate: content is required
	if exec.Goals.Content == "" {
		return fmt.Errorf("goals agent (%s) returned empty content", agentID)
	}

	// Update Name from goals content (extract first line as execution title)
	if goalName := extractGoalName(exec.Goals); goalName != "" {
		e.updateUIFields(ctx, exec, goalName, "")
	}

	return nil
}

// ParseDelivery converts map to DeliveryTarget struct
// Returns nil if data is nil or type is invalid/missing
func ParseDelivery(data map[string]interface{}) *robottypes.DeliveryTarget {
	if data == nil {
		return nil
	}

	// Type is required - if missing or invalid, return nil
	t, ok := data["type"].(string)
	if !ok || t == "" {
		return nil
	}

	deliveryType := robottypes.DeliveryType(t)
	if !IsValidDeliveryType(deliveryType) {
		// Invalid type - return nil to indicate parsing failure
		return nil
	}

	target := &robottypes.DeliveryTarget{
		Type: deliveryType,
	}

	// Parse recipients
	if recipients, ok := data["recipients"].([]interface{}); ok {
		for _, r := range recipients {
			if s, ok := r.(string); ok {
				target.Recipients = append(target.Recipients, s)
			}
		}
	}

	// Parse format
	if format, ok := data["format"].(string); ok {
		target.Format = format
	}

	// Parse template
	if template, ok := data["template"].(string); ok {
		target.Template = template
	}

	// Parse options
	if options, ok := data["options"].(map[string]interface{}); ok {
		target.Options = options
	}

	return target
}

// IsValidDeliveryType checks if the delivery type is valid
func IsValidDeliveryType(t robottypes.DeliveryType) bool {
	switch t {
	case robottypes.DeliveryEmail, robottypes.DeliveryWebhook,
		robottypes.DeliveryProcess, robottypes.DeliveryNotify:
		return true
	default:
		return false
	}
}
