package app

import (
	"context"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	"github.com/tidbcloud/AgentTeam/internal/store/team"
)

func (store *Store) attachAgentTeamInvokeContexts(ctx context.Context, tx coredb.Tx, workspaceID string, target message.ThreadAccess, authorMemberID string, teamID string, plan message.AgentRoutingPlan) (message.AgentRoutingPlan, error) {
	teamID = strings.TrimSpace(teamID)
	if teamID == "" {
		return plan, nil
	}
	if target.ThreadType == domain.ThreadTypeDM {
		return message.AgentRoutingPlan{}, badRequest("teamInvokeTeamId is not supported for DM targets")
	}
	mentionScopeID := strings.TrimSpace(target.ChannelID)
	if target.ReplyThread != nil && strings.TrimSpace(target.ReplyThread.ParentChannelID) != "" {
		mentionScopeID = strings.TrimSpace(target.ReplyThread.ParentChannelID)
	}
	if mentionScopeID == "" {
		return message.AgentRoutingPlan{}, badRequest("teamInvokeTeamId requires a channel, reply thread, or task scope target")
	}
	if err := requireAgentTeamInvokeMembership(ctx, tx, workspaceID, teamID, authorMemberID); err != nil {
		return message.AgentRoutingPlan{}, err
	}

	directRecipients := agentDirectMentionMetadataRecipients(plan.DurableRecipients)
	if len(directRecipients) == 0 {
		return message.AgentRoutingPlan{}, badRequest("teamInvokeTeamId requires directly mentioning at least one agent member of the Team")
	}

	edges, err := team.New(store.db).InTx(tx).ExpandTeamInvocationForScope(ctx, team.ExpandTeamInvocationParams{
		WorkspaceID:    workspaceID,
		ScopeID:        mentionScopeID,
		AuthorMemberID: authorMemberID,
		TeamID:         teamID,
	})
	if err != nil {
		return message.AgentRoutingPlan{}, err
	}
	contextRecipients, err := teamInvokeContextRecipients(edges, directRecipients)
	if err != nil {
		return message.AgentRoutingPlan{}, err
	}
	return message.AgentRoutingPlan{
		DurableRecipients: message.MergeAgentRecipients(plan.DurableRecipients, contextRecipients),
		WakeRecipients:    message.MergeAgentRecipients(plan.WakeRecipients, contextRecipients),
	}, nil
}

func requireAgentTeamInvokeMembership(ctx context.Context, tx coredb.Tx, workspaceID string, teamID string, agentMemberID string) error {
	workspaceID = strings.TrimSpace(workspaceID)
	teamID = strings.TrimSpace(teamID)
	agentMemberID = strings.TrimSpace(agentMemberID)

	var exists int
	err := tx.QueryRow(ctx, `
SELECT 1
FROM workspace_teams
WHERE workspace_id = $1
  AND id = $2
  AND status = 'active'
LIMIT 1
`, workspaceID, teamID).Scan(&exists)
	if err != nil {
		if coredb.IsNotFound(err) {
			return badRequest("teamInvokeTeamId must be an active Team id in the current workspace; use the team id shown in the Team context, not the @handle")
		}
		return fmt.Errorf("validate team invoke team: %w", err)
	}

	err = tx.QueryRow(ctx, `
SELECT 1
FROM workspace_team_members
WHERE workspace_id = $1
  AND team_id = $2
  AND actor_kind = 'agent'
  AND actor_id = $3
  AND removed_at IS NULL
LIMIT 1
`, workspaceID, teamID, agentMemberID).Scan(&exists)
	if err != nil {
		if coredb.IsNotFound(err) {
			return badRequest("teamInvokeTeamId requires the sender to be an agent member of the Team")
		}
		return fmt.Errorf("validate team invoke sender: %w", err)
	}
	return nil
}

func teamInvokeContextRecipients(edges []team.TeamExpansionEdge, directRecipients []message.AgentRecipient) ([]message.AgentRecipient, error) {
	if len(edges) == 0 {
		return nil, badRequest("teamInvokeTeamId did not resolve any visible Team members")
	}
	rosterByTeam := make(map[string][]message.TeamDeliveryRosterMember)
	agentEdgesByMember := make(map[string]team.TeamExpansionEdge)
	for _, edge := range edges {
		teamID := strings.TrimSpace(edge.TeamID)
		if teamID == "" {
			continue
		}
		rosterByTeam[teamID] = append(rosterByTeam[teamID], message.TeamDeliveryRosterMember{
			TeamMemberID:      edge.TeamMemberID,
			ActorKind:         edge.ActorKind,
			ActorID:           edge.ActorID,
			DisplayName:       edge.ActorDisplayName,
			Handle:            edge.ActorHandle,
			Role:              edge.Role,
			RoleLabel:         edge.RoleLabel,
			RoleOverlayPrompt: edge.RoleOverlaySnapshot,
			WakeOnTeamMention: edge.WakeOnTeamMention,
			DeliveryKind:      edge.ActorKind,
		})
		if edge.ActorKind != team.ActorKindAgent {
			continue
		}
		agentMemberID := strings.TrimSpace(edge.ActorID)
		if agentMemberID == "" || strings.TrimSpace(edge.AgentID) == "" {
			continue
		}
		agentEdgesByMember[agentMemberID] = edge
	}

	recipients := make([]message.AgentRecipient, 0, len(directRecipients))
	for _, direct := range directRecipients {
		agentMemberID := strings.TrimSpace(direct.AgentMemberID)
		edge, ok := agentEdgesByMember[agentMemberID]
		if !ok {
			return nil, badRequest("teamInvokeTeamId direct mentions must target agent members of the Team")
		}
		teamID := strings.TrimSpace(edge.TeamID)
		recipients = append(recipients, message.AgentRecipient{
			AgentID:         firstNonEmpty(strings.TrimSpace(direct.AgentID), edge.AgentID),
			MachineID:       firstNonEmpty(strings.TrimSpace(direct.MachineID), edge.MachineID),
			AgentMemberID:   agentMemberID,
			AutoWakeEnabled: direct.AutoWakeEnabled || edge.AutoWakeEnabled,
			DeliveryReason:  message.AgentDeliveryReasonDirectMention,
			MentionToken:    direct.MentionToken,
			TeamContexts: []message.TeamDeliveryContext{{
				TeamID:                    teamID,
				TeamHandle:                edge.TeamHandle,
				TeamDisplayName:           edge.TeamDisplayName,
				TeamDescription:           edge.TeamDescription,
				TeamMemberID:              edge.TeamMemberID,
				Role:                      edge.Role,
				RoleLabel:                 edge.RoleLabel,
				RoleOverlayPromptSnapshot: edge.RoleOverlaySnapshot,
				RoleOverlayVersion:        edge.RoleOverlayVersion,
				MemberRoster:              rosterWithSelf(rosterByTeam[teamID], agentMemberID),
			}},
		})
	}
	if len(recipients) == 0 {
		return nil, badRequest("teamInvokeTeamId requires directly mentioning at least one agent member of the Team")
	}
	return message.MergeAgentRecipients(recipients), nil
}
