package privateagent

import (
	"context"
	"fmt"
	"strings"

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

func scanTaskScopeNotificationAgents(rows coredb.Rows, defaultWakeState string) ([]message.ScopeNotificationAgentRow, error) {
	return scanScopeNotificationAgentsWithVisibility(rows, defaultWakeState)
}

// scanScopeNotificationAgentsWithVisibility reads rows that include the
// visibility column from the UNION ALL and task owned-agent queries.
func scanScopeNotificationAgentsWithVisibility(rows coredb.Rows, defaultWakeState string) ([]message.ScopeNotificationAgentRow, error) {
	agents := make([]message.ScopeNotificationAgentRow, 0)
	for rows.Next() {
		var agent message.ScopeNotificationAgentRow
		if err := rows.Scan(&agent.AgentID, &agent.AgentMemberID, &agent.Name, &agent.DisplayName, &agent.WakeState, &agent.Visibility); err != nil {
			return nil, fmt.Errorf("scan surface notification agent row: %w", err)
		}
		if normalizedState, err := normalizeAgentWakeState(agent.WakeState); err == nil {
			agent.WakeState = normalizedState
		} else {
			agent.WakeState = defaultWakeState
		}
		agents = append(agents, agent)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate surface notification agents: %w", err)
	}
	return agents, nil
}

// scanScopeNotificationAgentsWithVisibilityAndSortKey reads rows that
// additionally include the DB-computed sort_key column. The sort_key is
// returned alongside the agents so the cursor encoder can use the same
// canonical value the database used for ORDER BY / WHERE — Go never re-derives
// the comparison key, which would risk diverging on whitespace or Unicode
// case-folding.
func scanScopeNotificationAgentsWithVisibilityAndSortKey(rows coredb.Rows, defaultWakeState string) ([]message.ScopeNotificationAgentRow, []string, error) {
	agents := make([]message.ScopeNotificationAgentRow, 0)
	sortKeys := make([]string, 0)
	for rows.Next() {
		var agent message.ScopeNotificationAgentRow
		var sortKey string
		if err := rows.Scan(&agent.AgentID, &agent.AgentMemberID, &agent.Name, &agent.DisplayName, &agent.WakeState, &agent.Visibility, &sortKey); err != nil {
			return nil, nil, fmt.Errorf("scan surface notification agent row: %w", err)
		}
		if normalizedState, err := normalizeAgentWakeState(agent.WakeState); err == nil {
			agent.WakeState = normalizedState
		} else {
			agent.WakeState = defaultWakeState
		}
		agents = append(agents, agent)
		sortKeys = append(sortKeys, sortKey)
	}
	if err := rows.Err(); err != nil {
		return nil, nil, fmt.Errorf("iterate surface notification agents: %w", err)
	}
	return agents, sortKeys, nil
}

func taskAgentParticipatesInTaskScope(ctx context.Context, queryer coredb.Queryer, taskScopeID string, ownerMemberID string, agentMemberID string) (bool, error) {
	row := queryer.QueryRow(ctx, `
SELECT 1
FROM scope_memberships sm
JOIN members m
  ON m.id = sm.member_id
 AND m.status = 'active'
WHERE sm.scope_id = $1
  AND sm.member_id = $2
  AND sm.removed_at IS NULL
  AND sm.archived_at IS NULL
  AND sm.effective_to_seq IS NULL
  AND (sm.visibility = 'public' OR (sm.visibility = 'private' AND sm.owner_member_id = $3))
LIMIT 1
`, strings.TrimSpace(taskScopeID), strings.TrimSpace(agentMemberID), strings.TrimSpace(ownerMemberID))

	var sentinel int
	if err := row.Scan(&sentinel); err != nil {
		if coredb.IsNotFound(err) {
			return false, nil
		}
		return false, fmt.Errorf("load task agent notification participation: %w", err)
	}
	return true, nil
}

func (store *Store) publicAgentParticipatesInThread(ctx context.Context, queryer coredb.Queryer, workspaceID string, threadChannelID string, hostChannelID string, agentMemberID string) (bool, error) {
	row := queryer.QueryRow(ctx, `
SELECT 1
FROM scope_memberships sm
WHERE sm.scope_id = $1
  AND sm.member_id = $2
  AND sm.visibility = 'public'
  AND sm.removed_at IS NULL
LIMIT 1
`, strings.TrimSpace(threadChannelID), strings.TrimSpace(agentMemberID))

	var sentinel int
	if err := row.Scan(&sentinel); err != nil {
		if coredb.IsNotFound(err) {
			return false, nil
		}
		return false, fmt.Errorf("load public agent thread notification participation: %w", err)
	}
	return true, nil
}

func (store *Store) privateAgentSubscribedToThread(ctx context.Context, queryer coredb.Queryer, workspaceID string, threadChannelID string, hostChannelID string, ownerMemberID string, agentMemberID string) (bool, error) {
	row := queryer.QueryRow(ctx, `
SELECT 1
FROM scope_memberships sm
WHERE sm.scope_id = $1
  AND sm.member_id = $3
  AND sm.owner_member_id = $2
  AND sm.visibility = 'private'
  AND sm.removed_at IS NULL
LIMIT 1
`, strings.TrimSpace(threadChannelID), strings.TrimSpace(ownerMemberID), strings.TrimSpace(agentMemberID))

	var sentinel int
	if err := row.Scan(&sentinel); err != nil {
		if coredb.IsNotFound(err) {
			return false, nil
		}
		return false, fmt.Errorf("load private agent thread notification subscription: %w", err)
	}
	return true, nil
}
