package message

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/access"
)

var messageDiagnosticsWorkspaceAccessAction = access.HumanAction{
	Resource: access.ResourceWorkspace,
	Action:   access.ActionAccess,
}

func (txs TxStore) loadMessageDiagnosticsWorkspaceMember(ctx context.Context, userID string, workspaceID string) (access.HumanWorkspaceMember, error) {
	membership, err := access.LoadAuthorizedHumanWorkspaceMember(ctx, txs.tx, userID, workspaceID, messageDiagnosticsWorkspaceAccessAction)
	if errors.Is(err, access.ErrForbidden) {
		return access.HumanWorkspaceMember{}, ErrForbidden
	}
	if err != nil {
		return access.HumanWorkspaceMember{}, fmt.Errorf("load authorized human workspace member: %w", err)
	}
	return membership, nil
}

type messageDeliveryDiagnosticsScope struct {
	message        MessageDeliveryDiagnosticsMessage
	workspaceID    string
	threadID       string
	parentMessage  string
	scopeKind      string
	scopeChannel   string
	scopeDMThread  string
	viewerMemberID string
}

type messageDeliveryDiagnosticsAgentState = DiagnosticsAgentState

func (txs TxStore) GetMessageDeliveryDiagnosticsForUser(ctx context.Context, userID string, workspaceID string, messageID string) (MessageDeliveryDiagnostics, error) {
	scope, err := txs.loadMessageDeliveryDiagnosticsScopeForUser(ctx, userID, workspaceID, messageID)
	if err != nil {
		return MessageDeliveryDiagnostics{}, err
	}

	currentScopeAgents, err := txs.loadMessageDeliveryDiagnosticsScopeAgents(ctx, scope)
	if err != nil {
		return MessageDeliveryDiagnostics{}, err
	}
	durableRecipients := DeriveMessageDiagnosticsRecipientsFromScope(scope.message, currentScopeAgents)
	routingMode := DetectMessageDeliveryRoutingMode(scope.message, durableRecipients)
	scope.message.RoutingMode = routingMode
	scope.message.RoutingExplanation = ExplainMessageDeliveryRouting(routingMode)
	contextScope := DetectMessageDeliveryContextScope(scope.message)
	scope.message.ContextScope = contextScope
	scope.message.ContextScopeExplanation = ExplainMessageDeliveryContextScope(contextScope)

	responseAgents := BuildMessageDeliveryDiagnosticsAgents(
		scope.message,
		routingMode,
		currentScopeAgents,
		durableRecipients,
		nil,
		BuildMessageDeliveryOwnerPrivateRelevantAgentIDs(routingMode, scope.message, currentScopeAgents),
	)

	wakeInferenceBasis := MessageDeliveryWakeInferenceCurrentRuntime
	return MessageDeliveryDiagnostics{
		Message:                  scope.message,
		WakeInferenceBasis:       wakeInferenceBasis,
		WakeInferenceExplanation: ExplainMessageDeliveryWakeInference(wakeInferenceBasis),
		Summary:                  BuildMessageDeliveryDiagnosticsSummary(responseAgents),
		Agents:                   responseAgents,
	}, nil
}

func (txs TxStore) loadMessageDeliveryDiagnosticsScopeForUser(ctx context.Context, userID string, workspaceID string, messageID string) (messageDeliveryDiagnosticsScope, error) {
	membership, err := txs.loadMessageDiagnosticsWorkspaceMember(ctx, userID, workspaceID)
	if err != nil {
		return messageDeliveryDiagnosticsScope{}, err
	}

	query, args, err := BuildMessageDiagnosticsScopeQuery(workspaceID, strings.TrimSpace(messageID), membership.Member.ID, userID)
	if err != nil {
		return messageDeliveryDiagnosticsScope{}, err
	}

	row := txs.tx.QueryRow(ctx, query, args...)

	var scope messageDeliveryDiagnosticsScope
	if err := row.Scan(
		&scope.message.ID,
		&scope.threadID,
		&scope.message.ChannelID,
		&scope.message.ChannelType,
		&scope.message.ParentChannelID,
		&scope.message.ParentChannelType,
		&scope.parentMessage,
		&scope.message.AuthorMemberID,
		&scope.message.AuthorUserID,
		&scope.message.AuthorType,
		&scope.message.MessageType,
		&scope.message.Content,
		&scope.message.CreatedAt,
	); err != nil {
		if coredb.IsNotFound(err) {
			return messageDeliveryDiagnosticsScope{}, ErrNotFound
		}
		return messageDeliveryDiagnosticsScope{}, fmt.Errorf("load message diagnostics scope: %w", err)
	}

	switch scope.message.ChannelType {
	case "dm":
		scope.scopeKind = "dm"
		scope.scopeDMThread = scope.message.ChannelID
	case "thread":
		scope.scopeKind = "thread"
		if scope.message.ParentChannelType == "dm" {
			scope.scopeDMThread = scope.message.ParentChannelID
		} else {
			scope.scopeChannel = scope.message.ParentChannelID
		}
	default:
		scope.scopeKind = "channel"
		scope.scopeChannel = scope.message.ChannelID
	}
	scope.workspaceID = workspaceID
	scope.viewerMemberID = membership.Member.ID

	return scope, nil
}

func (txs TxStore) loadMessageDeliveryDiagnosticsScopeAgents(ctx context.Context, scope messageDeliveryDiagnosticsScope) ([]DiagnosticsAgentState, error) {
	return txs.loadScopeDiagnosticsAgents(ctx, scope.workspaceID, scope.message.ChannelID)
}

func (txs TxStore) loadMessageDeliveryDiagnosticsOwnerQuoteVisibilityAgents(ctx context.Context, scope messageDeliveryDiagnosticsScope) ([]DiagnosticsAgentState, error) {
	return nil, nil
}

func (txs TxStore) loadMessageDeliveryDiagnosticsSourceDurableRecipients(ctx context.Context, messageID string) ([]messageDeliveryDiagnosticsAgentState, error) {
	return nil, nil
}

func (txs TxStore) loadScopeDiagnosticsAgents(ctx context.Context, workspaceID string, scopeID string) ([]DiagnosticsAgentState, error) {
	rows, err := txs.tx.Query(ctx, `
SELECT
    a.id,
    a.name,
    a.member_id,
    a.machine_id,
    COALESCE(mac.name, ''),
    mac.created_by_user_id,
    'inactive',
    'offline',
    '',
    CASE WHEN sns.state = 'auto-wake' THEN 1 ELSE 0 END AS auto_wake_enabled,
    COALESCE(sns.state, '') AS subscription_state,
    FALSE AS machine_online
FROM scope_memberships sm
JOIN agents a
  ON a.workspace_id = $1
 AND a.member_id = sm.member_id
 AND a.status <> 'archived'
JOIN machines mac
  ON mac.workspace_id = a.workspace_id
 AND mac.id = a.machine_id
 AND mac.status = 'active'
LEFT JOIN scope_notification_settings sns
  ON sns.scope_id = sm.scope_id
 AND sns.member_id = sm.member_id
WHERE sm.scope_id = $2
  AND sm.removed_at IS NULL
ORDER BY a.name ASC, a.member_id ASC, a.id ASC
`, strings.TrimSpace(workspaceID), strings.TrimSpace(scopeID))
	if err != nil {
		return nil, fmt.Errorf("load scope diagnostics agents: %w", err)
	}
	defer rows.Close()

	agents := make([]DiagnosticsAgentState, 0)
	for rows.Next() {
		var agent DiagnosticsAgentState
		if err := rows.Scan(
			&agent.AgentID,
			&agent.AgentName,
			&agent.MemberID,
			&agent.MachineID,
			&agent.MachineName,
			&agent.OwnerUserID,
			&agent.AgentStatus,
			&agent.AgentActivity,
			&agent.ActivityDetail,
			&agent.AutoWakeEnabled,
			&agent.SubscriptionState,
			&agent.MachineOnline,
		); err != nil {
			return nil, fmt.Errorf("scan scope diagnostics agent: %w", err)
		}
		agents = append(agents, agent)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate scope diagnostics agents: %w", err)
	}
	return agents, nil
}
