// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/repo/blockedemail/blockedemail.go
package blockedemail

import (
	"context"
	"strings"

	"go.uber.org/zap"

	"github.com/gocql/gocql"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
	dom_blockedemail "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/blockedemail"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/validation"
)

type blockedEmailRepositoryImpl struct {
	config  *config.Configuration
	logger  *zap.Logger
	session *gocql.Session
}

func NewBlockedEmailRepository(
	config *config.Configuration,
	logger *zap.Logger,
	session *gocql.Session,
) dom_blockedemail.BlockedEmailRepository {
	logger = logger.Named("BlockedEmailRepository")
	return &blockedEmailRepositoryImpl{
		config:  config,
		logger:  logger,
		session: session,
	}
}

func (r *blockedEmailRepositoryImpl) Create(ctx context.Context, blockedEmail *dom_blockedemail.BlockedEmail) error {
	// Normalize email to lowercase
	normalizedEmail := strings.ToLower(strings.TrimSpace(blockedEmail.BlockedEmail))

	query := `INSERT INTO user_blocked_emails (
		user_id, blocked_email, blocked_user_id, reason, created_at
	) VALUES (?, ?, ?, ?, ?)`

	err := r.session.Query(query,
		blockedEmail.UserID,
		normalizedEmail,
		blockedEmail.BlockedUserID,
		blockedEmail.Reason,
		blockedEmail.CreatedAt,
	).WithContext(ctx).Exec()

	if err != nil {
		r.logger.Error("Failed to create blocked email",
			zap.Any("error", err),
			zap.Any("user_id", blockedEmail.UserID),
			zap.String("blocked_email", validation.MaskEmail(normalizedEmail)))
		return err
	}

	r.logger.Debug("Blocked email created",
		zap.Any("user_id", blockedEmail.UserID),
		zap.String("blocked_email", validation.MaskEmail(normalizedEmail)))

	return nil
}

func (r *blockedEmailRepositoryImpl) Get(ctx context.Context, userID gocql.UUID, blockedEmail string) (*dom_blockedemail.BlockedEmail, error) {
	// Normalize email to lowercase
	normalizedEmail := strings.ToLower(strings.TrimSpace(blockedEmail))

	query := `SELECT user_id, blocked_email, blocked_user_id, reason, created_at
		FROM user_blocked_emails
		WHERE user_id = ? AND blocked_email = ?`

	var result dom_blockedemail.BlockedEmail
	err := r.session.Query(query, userID, normalizedEmail).
		WithContext(ctx).
		Scan(
			&result.UserID,
			&result.BlockedEmail,
			&result.BlockedUserID,
			&result.Reason,
			&result.CreatedAt,
		)

	if err != nil {
		if err == gocql.ErrNotFound {
			return nil, nil
		}
		r.logger.Error("Failed to get blocked email",
			zap.Any("error", err),
			zap.Any("user_id", userID),
			zap.String("blocked_email", validation.MaskEmail(normalizedEmail)))
		return nil, err
	}

	return &result, nil
}

func (r *blockedEmailRepositoryImpl) List(ctx context.Context, userID gocql.UUID) ([]*dom_blockedemail.BlockedEmail, error) {
	query := `SELECT user_id, blocked_email, blocked_user_id, reason, created_at
		FROM user_blocked_emails
		WHERE user_id = ?`

	iter := r.session.Query(query, userID).WithContext(ctx).Iter()

	var results []*dom_blockedemail.BlockedEmail
	var entry dom_blockedemail.BlockedEmail

	for iter.Scan(
		&entry.UserID,
		&entry.BlockedEmail,
		&entry.BlockedUserID,
		&entry.Reason,
		&entry.CreatedAt,
	) {
		results = append(results, &dom_blockedemail.BlockedEmail{
			UserID:        entry.UserID,
			BlockedEmail:  entry.BlockedEmail,
			BlockedUserID: entry.BlockedUserID,
			Reason:        entry.Reason,
			CreatedAt:     entry.CreatedAt,
		})
	}

	if err := iter.Close(); err != nil {
		r.logger.Error("Failed to list blocked emails",
			zap.Any("error", err),
			zap.Any("user_id", userID))
		return nil, err
	}

	r.logger.Debug("Listed blocked emails",
		zap.Any("user_id", userID),
		zap.Int("count", len(results)))

	return results, nil
}

func (r *blockedEmailRepositoryImpl) Delete(ctx context.Context, userID gocql.UUID, blockedEmail string) error {
	// Normalize email to lowercase
	normalizedEmail := strings.ToLower(strings.TrimSpace(blockedEmail))

	query := `DELETE FROM user_blocked_emails WHERE user_id = ? AND blocked_email = ?`

	err := r.session.Query(query, userID, normalizedEmail).WithContext(ctx).Exec()
	if err != nil {
		r.logger.Error("Failed to delete blocked email",
			zap.Any("error", err),
			zap.Any("user_id", userID),
			zap.String("blocked_email", validation.MaskEmail(normalizedEmail)))
		return err
	}

	r.logger.Debug("Blocked email deleted",
		zap.Any("user_id", userID),
		zap.String("blocked_email", validation.MaskEmail(normalizedEmail)))

	return nil
}

func (r *blockedEmailRepositoryImpl) IsBlocked(ctx context.Context, userID gocql.UUID, email string) (bool, error) {
	// Normalize email to lowercase
	normalizedEmail := strings.ToLower(strings.TrimSpace(email))

	query := `SELECT blocked_email FROM user_blocked_emails WHERE user_id = ? AND blocked_email = ?`

	var blockedEmail string
	err := r.session.Query(query, userID, normalizedEmail).
		WithContext(ctx).
		Scan(&blockedEmail)

	if err != nil {
		if err == gocql.ErrNotFound {
			return false, nil
		}
		r.logger.Error("Failed to check if email is blocked",
			zap.Any("error", err),
			zap.Any("user_id", userID),
			zap.String("email", validation.MaskEmail(normalizedEmail)))
		return false, err
	}

	return true, nil
}

func (r *blockedEmailRepositoryImpl) Count(ctx context.Context, userID gocql.UUID) (int, error) {
	query := `SELECT COUNT(*) FROM user_blocked_emails WHERE user_id = ?`

	var count int
	err := r.session.Query(query, userID).WithContext(ctx).Scan(&count)
	if err != nil {
		r.logger.Error("Failed to count blocked emails",
			zap.Any("error", err),
			zap.Any("user_id", userID))
		return 0, err
	}

	return count, nil
}
