package discovery

import (
	"context"
	"fmt"
	"strings"

	"github.com/jackc/pgx/v5"
)

func (s *Service) relatedProfileIDs(ctx context.Context, externalUserID, profileID string) ([]string, error) {
	var groupID *string
	err := s.db.QueryRow(ctx, `
		SELECT person_group_id::text
		FROM contact_discovered_profiles p
		JOIN users u ON p.user_id = u.id
		WHERE u.external_id = $1 AND p.id = $2::uuid
	`, externalUserID, profileID).Scan(&groupID)
	if err != nil {
		if err == pgx.ErrNoRows {
			return nil, fmt.Errorf("profile not found")
		}
		return nil, err
	}
	if groupID != nil && strings.TrimSpace(*groupID) != "" {
		rows, err := s.db.Query(ctx, `
			SELECT p.id::text
			FROM contact_discovered_profiles p
			JOIN users u ON p.user_id = u.id
			WHERE u.external_id = $1 AND p.person_group_id = $2::uuid
		`, externalUserID, *groupID)
		if err != nil {
			return nil, err
		}
		defer rows.Close()
		var ids []string
		for rows.Next() {
			var id string
			if err := rows.Scan(&id); err != nil {
				return nil, err
			}
			ids = append(ids, id)
		}
		if len(ids) > 0 {
			return ids, rows.Err()
		}
	}
	return []string{profileID}, nil
}

func (s *Service) profileEmails(ctx context.Context, externalUserID string, profileIDs []string) ([]string, error) {
	rows, err := s.db.Query(ctx, `
		SELECT primary_email
		FROM contact_discovered_profiles p
		JOIN users u ON p.user_id = u.id
		WHERE u.external_id = $1 AND p.id = ANY($2::uuid[])
	`, externalUserID, profileIDs)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	seen := map[string]struct{}{}
	var emails []string
	for rows.Next() {
		var email string
		if err := rows.Scan(&email); err != nil {
			return nil, err
		}
		low := strings.ToLower(strings.TrimSpace(email))
		if low == "" {
			continue
		}
		if _, ok := seen[low]; ok {
			continue
		}
		seen[low] = struct{}{}
		emails = append(emails, low)
	}
	return emails, rows.Err()
}

func (s *Service) persistEmailRejections(ctx context.Context, externalUserID string, emails []string, rejectionType string) {
	for _, email := range emails {
		_, _ = s.db.Exec(ctx, `
			INSERT INTO contact_discovery_rejections (user_id, rejection_key, rejection_type)
			VALUES ((SELECT id FROM users WHERE external_id = $1), $2, $3)
			ON CONFLICT DO NOTHING
		`, externalUserID, "email:"+email, rejectionType)
	}
}

func (s *Service) rejectPendingSuggestions(ctx context.Context, externalUserID string, profileIDs []string) {
	_, _ = s.db.Exec(ctx, `
		UPDATE contact_enrichment_suggestions
		SET status = 'rejected', rejected_at = NOW()
		WHERE user_id = (SELECT id FROM users WHERE external_id = $1)
		  AND profile_id = ANY($2::uuid[])
		  AND status = 'pending'
	`, externalUserID, profileIDs)
}

func (s *Service) IgnoreProfile(ctx context.Context, externalUserID, profileID string) ([]string, error) {
	ids, err := s.relatedProfileIDs(ctx, externalUserID, profileID)
	if err != nil {
		return nil, err
	}
	_, err = s.db.Exec(ctx, `
		UPDATE contact_discovered_profiles
		SET status = 'ignored', ignored_at = NOW(), updated_at = NOW()
		WHERE user_id = (SELECT id FROM users WHERE external_id = $1)
		  AND id = ANY($2::uuid[])
	`, externalUserID, ids)
	if err != nil {
		return nil, err
	}
	emails, err := s.profileEmails(ctx, externalUserID, ids)
	if err != nil {
		return nil, err
	}
	s.persistEmailRejections(ctx, externalUserID, emails, "ignore")
	s.rejectPendingSuggestions(ctx, externalUserID, ids)
	return emails, nil
}

func (s *Service) BlockProfile(ctx context.Context, externalUserID, profileID string) ([]string, error) {
	ids, err := s.relatedProfileIDs(ctx, externalUserID, profileID)
	if err != nil {
		return nil, err
	}
	_, err = s.db.Exec(ctx, `
		UPDATE contact_discovered_profiles
		SET status = 'blocked',
		    blocked_at = NOW(),
		    user_blocked = true,
		    is_spam_heavy = true,
		    updated_at = NOW()
		WHERE user_id = (SELECT id FROM users WHERE external_id = $1)
		  AND id = ANY($2::uuid[])
	`, externalUserID, ids)
	if err != nil {
		return nil, err
	}
	emails, err := s.profileEmails(ctx, externalUserID, ids)
	if err != nil {
		return nil, err
	}
	s.persistEmailRejections(ctx, externalUserID, emails, "block")
	s.rejectPendingSuggestions(ctx, externalUserID, ids)
	return emails, nil
}

func (s *Service) ListProfilesByStatus(ctx context.Context, externalUserID string, status ProfileStatus) ([]Profile, error) {
	rows, err := s.db.Query(ctx, `
		SELECT `+profileSelectColumns+`
		FROM contact_discovered_profiles p
		JOIN users u ON p.user_id = u.id
		WHERE u.external_id = $1 AND p.status = $2
		ORDER BY `+profileInteractionOrderBy+`
	`, externalUserID, status)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var profiles []Profile
	for rows.Next() {
		p, err := scanProfileRow(rows)
		if err != nil {
			return nil, err
		}
		profiles = append(profiles, p)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	s.attachSignaturesToProfiles(ctx, profiles)
	return profiles, nil
}

func (s *Service) ListOtherProfileGroups(ctx context.Context, externalUserID string) ([]ProfileGroup, error) {
	page, err := s.ListOtherProfileGroupsPage(ctx, externalUserID, MaxOtherGroupsPageSize, 0, "")
	if err != nil {
		return nil, err
	}
	if page.Total <= page.Limit {
		return page.Groups, nil
	}
	all := append([]ProfileGroup{}, page.Groups...)
	for offset := page.Limit; offset < page.Total; offset += MaxOtherGroupsPageSize {
		next, err := s.ListOtherProfileGroupsPage(ctx, externalUserID, MaxOtherGroupsPageSize, offset, "")
		if err != nil {
			return nil, err
		}
		all = append(all, next.Groups...)
	}
	return all, nil
}
