package gateway

import (
	"context"
	"crypto/rand"
	"math/big"
	"time"

	"go.uber.org/zap"

	domaintenant "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/tenant"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/logger"
)

// CheckTenantSlugAvailabilityUseCase checks if a tenant slug is available
type CheckTenantSlugAvailabilityUseCase struct {
	tenantRepo domaintenant.Repository
	logger     *zap.Logger
}

// ProvideCheckTenantSlugAvailabilityUseCase creates a new CheckTenantSlugAvailabilityUseCase
func ProvideCheckTenantSlugAvailabilityUseCase(
	tenantRepo domaintenant.Repository,
	logger *zap.Logger,
) *CheckTenantSlugAvailabilityUseCase {
	return &CheckTenantSlugAvailabilityUseCase{
		tenantRepo: tenantRepo,
		logger:     logger.Named("check-tenant-slug-availability-usecase"),
	}
}

// Execute checks if a tenant slug is available (not already taken)
// CWE-203: Implements timing attack mitigation to prevent tenant enumeration
func (uc *CheckTenantSlugAvailabilityUseCase) Execute(ctx context.Context, slug string) error {
	// Record start time for timing attack mitigation
	startTime := time.Now()

	// Always perform the database lookup
	existingTenant, err := uc.tenantRepo.GetBySlug(ctx, slug)

	// Store the result but don't return early - prevents timing leaks
	var resultError error

	if err == nil && existingTenant != nil {
		// CWE-532: Use redacted tenant slug for logging
		uc.logger.Warn("tenant slug already exists",
			logger.TenantSlugHash(slug),
			logger.SafeTenantSlug("tenant_slug_redacted", slug))
		resultError = domaintenant.ErrTenantExists
	} else if err != nil && err != domaintenant.ErrTenantNotFound {
		// Real database error (not "not found")
		uc.logger.Error("failed to check tenant existence", zap.Error(err))
		resultError = err
	} else {
		// CWE-532: Use redacted tenant slug for logging
		// Slug is available (err == ErrTenantNotFound or no error with nil tenant)
		uc.logger.Debug("tenant slug is available",
			logger.TenantSlugHash(slug),
			logger.SafeTenantSlug("tenant_slug_redacted", slug))
		resultError = nil
	}

	// CWE-203: Add random delay to prevent timing attacks
	// Ensures response time is similar whether tenant exists or not
	elapsed := time.Since(startTime)
	minResponseTime := 50 * time.Millisecond // Minimum response time
	maxJitter := 30 * time.Millisecond       // Random jitter to add unpredictability

	// Generate cryptographically secure random jitter
	jitterMs, _ := rand.Int(rand.Reader, big.NewInt(int64(maxJitter.Milliseconds())))
	jitter := time.Duration(jitterMs.Int64()) * time.Millisecond

	targetDelay := minResponseTime + jitter
	if elapsed < targetDelay {
		time.Sleep(targetDelay - elapsed)
	}

	return resultError
}
