package tools

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/google/uuid"

	"github.com/nextlevelbuilder/goclaw/internal/bus"
	"github.com/nextlevelbuilder/goclaw/internal/store"
	"github.com/nextlevelbuilder/goclaw/pkg/protocol"
)

// broadcastTeamEvent sends a real-time event via the message bus for team activity visibility.
func (m *TeamToolManager) broadcastTeamEvent(name string, payload any) {
	if m.msgBus == nil {
		return
	}
	m.msgBus.Broadcast(bus.Event{
		Name:    name,
		Payload: payload,
	})
}

// resolveTeamRole returns the calling agent's role in the team.
// Unlike requireLead(), this does NOT bypass for teammate channel —
// workspace RBAC must respect actual roles even for teammate agents.
func (m *TeamToolManager) resolveTeamRole(ctx context.Context, team *store.TeamData, agentID uuid.UUID) (string, error) {
	if agentID == team.LeadAgentID {
		return store.TeamRoleLead, nil
	}
	members, err := m.cachedListMembers(ctx, team.ID, agentID)
	if err != nil {
		return "", fmt.Errorf("failed to resolve team role: %w", err)
	}
	for _, member := range members {
		if member.AgentID == agentID {
			return member.Role, nil
		}
	}
	return "", fmt.Errorf("agent is not a member of this team")
}

// agentDisplayName returns the display name for an agent key, falling back to empty string.
func (m *TeamToolManager) agentDisplayName(ctx context.Context, key string) string {
	ag, err := m.cachedGetAgentByKey(ctx, key)
	if err != nil || ag.DisplayName == "" {
		return ""
	}
	return ag.DisplayName
}

// ============================================================
// Version helpers
// ============================================================

// IsTeamV2 checks if team has version >= 2 in settings.
// Returns false for nil team, nil/empty settings, or version < 2.
func IsTeamV2(team *store.TeamData) bool {
	if team == nil || team.Settings == nil {
		return false
	}
	var s struct {
		Version int `json:"version"`
	}
	if json.Unmarshal(team.Settings, &s) != nil {
		return false
	}
	return s.Version >= 2
}

// ============================================================
// Follow-up settings helpers
// ============================================================

const (
	defaultFollowupDelayMinutes = 30
	defaultFollowupMaxReminders = 0 // 0 = unlimited
)

// followupDelayMinutes returns the team's followup_interval_minutes setting, or the default.
// Returns 0 for v1 teams (followup disabled).
func (m *TeamToolManager) followupDelayMinutes(team *store.TeamData) int {
	if !IsTeamV2(team) {
		return 0
	}
	if team.Settings == nil {
		return defaultFollowupDelayMinutes
	}
	var settings map[string]any
	if json.Unmarshal(team.Settings, &settings) != nil {
		return defaultFollowupDelayMinutes
	}
	if v, ok := settings["followup_interval_minutes"].(float64); ok && v > 0 {
		return int(v)
	}
	return defaultFollowupDelayMinutes
}

// followupMaxReminders returns the team's followup_max_reminders setting, or the default.
// Returns 0 for v1 teams (followup disabled).
func (m *TeamToolManager) followupMaxReminders(team *store.TeamData) int {
	if !IsTeamV2(team) {
		return 0
	}
	if team.Settings == nil {
		return defaultFollowupMaxReminders
	}
	var settings map[string]any
	if json.Unmarshal(team.Settings, &settings) != nil {
		return defaultFollowupMaxReminders
	}
	if v, ok := settings["followup_max_reminders"].(float64); ok && v >= 0 {
		return int(v)
	}
	return defaultFollowupMaxReminders
}

// ============================================================
// Escalation policy
// ============================================================

// EscalationResult indicates how an action should be handled.
type EscalationResult int

const (
	EscalationNone   EscalationResult = iota // no escalation configured
	EscalationAuto                           // LLM chooses (currently: always review)
	EscalationReview                         // create review task
	EscalationReject                         // reject outright
)

// checkEscalation parses the team's escalation_mode and escalation_actions settings.
// Returns EscalationNone for v1 teams.
func (m *TeamToolManager) checkEscalation(team *store.TeamData, action string) EscalationResult {
	if !IsTeamV2(team) {
		return EscalationNone
	}
	if team.Settings == nil {
		return EscalationNone
	}
	var settings map[string]any
	if err := json.Unmarshal(team.Settings, &settings); err != nil {
		return EscalationNone
	}

	mode, _ := settings["escalation_mode"].(string)
	if mode == "" {
		return EscalationNone
	}

	// Check if action is in escalation_actions list.
	actionsRaw, _ := settings["escalation_actions"].([]any)
	if len(actionsRaw) > 0 {
		found := false
		for _, a := range actionsRaw {
			if s, ok := a.(string); ok && s == action {
				found = true
				break
			}
		}
		if !found {
			return EscalationNone
		}
	}

	switch mode {
	case "auto":
		return EscalationAuto
	case "review":
		return EscalationReview
	case "reject":
		return EscalationReject
	default:
		return EscalationNone
	}
}

// createEscalationTask creates an escalation task and broadcasts the event.
func (m *TeamToolManager) createEscalationTask(ctx context.Context, team *store.TeamData, agentID uuid.UUID, subject, description string) *Result {
	task := &store.TeamTaskData{
		TeamID:           team.ID,
		Subject:          subject,
		Description:      description,
		Status:           store.TeamTaskStatusPending,
		UserID:           store.UserIDFromContext(ctx),
		Channel:          ToolChannelFromCtx(ctx),
		TaskType:         "escalation",
		CreatedByAgentID: &agentID,
		ChatID:           ToolChatIDFromCtx(ctx),
	}
	if err := m.teamStore.CreateTask(ctx, task); err != nil {
		return ErrorResult("failed to create escalation task: " + err.Error())
	}

	m.broadcastTeamEvent(protocol.EventTeamTaskCreated, protocol.TeamTaskEventPayload{
		TeamID:    team.ID.String(),
		TaskID:    task.ID.String(),
		Subject:   subject,
		Status:    store.TeamTaskStatusPending,
		UserID:    store.UserIDFromContext(ctx),
		Channel:   ToolChannelFromCtx(ctx),
		ChatID:    ToolChatIDFromCtx(ctx),
		Timestamp: task.CreatedAt.UTC().Format("2006-01-02T15:04:05Z"),
	})

	// Notify channel if possible.
	m.notifyChannelReview(task)

	return NewResult(fmt.Sprintf("Action requires approval. Escalation task created: %s (id=%s). A human must approve before this action can proceed.", subject, task.Identifier))
}

// notifyChannelReview publishes an outbound message to the origin channel about a pending review.
func (m *TeamToolManager) notifyChannelReview(task *store.TeamTaskData) {
	if m.msgBus == nil || task.Channel == "" || task.ChatID == "" {
		return
	}
	content := fmt.Sprintf("🔔 Escalation: \"%s\" requires human review (task %s).", task.Subject, task.Identifier)
	m.msgBus.PublishOutbound(bus.OutboundMessage{
		Channel: task.Channel,
		ChatID:  task.ChatID,
		Content: content,
	})
}
