package taskboard

import (
	"context"
	"fmt"
	"strings"
	"time"

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

// ensureTaskDiscussionScopeParticipants materializes visibility only. Joining a
// Task does not mean watching it: Humans default to mute (Unwatch), Agents
// default to no-wake, and existing choices are never overwritten.
func (txs TxStore) ensureTaskDiscussionScopeParticipants(ctx context.Context, execer coredb.QueryExecer, _ string, taskScopeID string, actorMemberID string, _ Task, mentionTargets []taskDiscussionMentionTarget, createdAt time.Time) error {
	trimmedTaskScopeID := strings.TrimSpace(taskScopeID)
	trimmedActorMemberID := strings.TrimSpace(actorMemberID)
	if trimmedTaskScopeID == "" || trimmedActorMemberID == "" {
		return fmt.Errorf("task discussion scope participants require task scope and actor")
	}
	memberIDs := []string{trimmedActorMemberID}
	for _, target := range mentionTargets {
		if memberID := strings.TrimSpace(target.MemberID); memberID != "" {
			memberIDs = append(memberIDs, memberID)
		}
	}
	return ensureTaskScopeParticipantsDefaultUnwatched(ctx, execer, trimmedTaskScopeID, memberIDs, createdAt)
}

func ensureTaskScopeParticipantsDefaultUnwatched(ctx context.Context, execer coredb.QueryExecer, taskScopeID string, memberIDs []string, createdAt time.Time) error {
	seen := make(map[string]struct{}, len(memberIDs))
	for _, memberID := range memberIDs {
		trimmedMemberID := strings.TrimSpace(memberID)
		if trimmedMemberID == "" {
			continue
		}
		if _, ok := seen[trimmedMemberID]; ok {
			continue
		}
		seen[trimmedMemberID] = struct{}{}
		if err := ensureTaskScopeParticipantDefaultUnwatched(ctx, execer, taskScopeID, trimmedMemberID, createdAt); err != nil {
			return err
		}
	}
	return nil
}

func ensureTaskCollaboratorScopeDefaultUnwatched(ctx context.Context, execer coredb.QueryExecer, target taskCollaboratorTarget, memberIDs []string, createdAt time.Time) error {
	if target.ScopeType != taskCollaboratorScopeTask {
		return nil
	}
	taskScopeID, err := loadTaskScopeIDForParticipantNotification(ctx, execer, target.TaskID)
	if err != nil {
		return err
	}
	return ensureTaskScopeParticipantsDefaultUnwatched(ctx, execer, taskScopeID, memberIDs, createdAt)
}

func loadTaskScopeIDForParticipantNotification(ctx context.Context, queryer coredb.Queryer, taskID string) (string, error) {
	trimmedTaskID := strings.TrimSpace(taskID)
	if trimmedTaskID == "" {
		return "", nil
	}
	var taskScopeID string
	if err := queryer.QueryRow(ctx, `
SELECT task_scope_id
FROM tasks
WHERE id = $1
`, trimmedTaskID).Scan(&taskScopeID); err != nil {
		if coredb.IsNotFound(err) {
			return "", nil
		}
		return "", fmt.Errorf("load task scope for participant notification: %w", err)
	}
	return strings.TrimSpace(taskScopeID), nil
}

func ensureTaskScopeParticipantDefaultUnwatched(ctx context.Context, execer coredb.QueryExecer, scopeID string, memberID string, createdAt time.Time) error {
	if strings.TrimSpace(scopeID) == "" || strings.TrimSpace(memberID) == "" {
		return nil
	}
	state, err := taskScopeDefaultUnwatchedNotificationStateForMember(ctx, execer, memberID)
	if err != nil {
		return err
	}
	return ensureTaskScopeParticipantNotification(ctx, execer, scopeID, memberID, state, createdAt)
}

func ensureTaskScopeParticipantNotification(ctx context.Context, execer coredb.QueryExecer, scopeID string, memberID string, state string, createdAt time.Time) error {
	trimmedScopeID := strings.TrimSpace(scopeID)
	trimmedMemberID := strings.TrimSpace(memberID)
	if trimmedScopeID == "" || trimmedMemberID == "" {
		return nil
	}
	if err := message.EnsureScopeMembership(ctx, execer, trimmedScopeID, trimmedMemberID, "public", "", "", 0, 0, createdAt); err != nil {
		return err
	}
	trimmedState := strings.TrimSpace(state)
	if trimmedState == "" {
		return nil
	}
	now := createdAt.UTC()
	if now.IsZero() {
		now = time.Now().UTC()
	}
	if _, err := execer.Exec(ctx, `
INSERT INTO scope_notification_settings (
  scope_id, member_id, state, task_watch_state,
  created_at, updated_at, unread_generation
) VALUES ($1, $2, $3, $3, $4, $4, 1)
ON DUPLICATE KEY UPDATE
  unread_generation = CASE
    WHEN task_watch_state IS NULL THEN unread_generation + 1
    ELSE unread_generation
  END,
  updated_at = CASE
    WHEN task_watch_state IS NULL THEN VALUES(updated_at)
    ELSE updated_at
  END,
  task_watch_state = COALESCE(task_watch_state, VALUES(task_watch_state))
`, trimmedScopeID, trimmedMemberID, trimmedState, now); err != nil {
		return fmt.Errorf("insert task scope default notification setting: %w", err)
	}
	return nil
}

func taskScopeDefaultUnwatchedNotificationStateForMember(ctx context.Context, queryer coredb.Queryer, memberID string) (string, error) {
	isAgent, err := taskScopeMemberIsAgent(ctx, queryer, memberID)
	if err != nil {
		return "", err
	}
	if isAgent {
		return "no-wake", nil
	}
	return "mute", nil
}

func taskScopeMemberIsAgent(ctx context.Context, queryer coredb.Queryer, memberID string) (bool, error) {
	var memberType string
	if err := queryer.QueryRow(ctx, `
SELECT type
FROM members
WHERE id = $1
`, strings.TrimSpace(memberID)).Scan(&memberType); err != nil {
		return false, fmt.Errorf("load task scope notification member type: %w", err)
	}
	return strings.TrimSpace(memberType) == string(domain.MemberTypeAgent), nil
}
