package services

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

	"go.uber.org/zap"
)

// CDNProvider represents the type of CDN provider
type CDNProvider string

const (
	CDNProviderCloudFront CDNProvider = "cloudfront"
	CDNProviderCloudflare CDNProvider = "cloudflare"
	CDNProviderGeneric    CDNProvider = "generic"
	CDNProviderNone       CDNProvider = "none"
)

// CDNConfig represents configuration for CDN service
type CDNConfig struct {
	Provider       CDNProvider
	BaseURL        string // CDN base URL (e.g., https://d1234567890.cloudfront.net)
	DistributionID string // CloudFront distribution ID (if applicable)
	APIKey         string // API key for cache invalidation (if applicable)
	Enabled        bool
	Logger         *zap.Logger
}

// CDNService provides CDN integration capabilities
// BE-SVC-013: Implement CDN integration
type CDNService struct {
	config CDNConfig
	logger *zap.Logger
}

// NewCDNService creates a new CDN service
func NewCDNService(config CDNConfig) *CDNService {
	if config.Logger == nil {
		config.Logger = zap.NewNop()
	}
	return &CDNService{
		config: config,
		logger: config.Logger,
	}
}

// GetURL generates a CDN URL for a given path
func (s *CDNService) GetURL(path string) string {
	if !s.config.Enabled || s.config.Provider == CDNProviderNone {
		return path // Return original path if CDN is disabled
	}

	// Remove leading slash if present
	path = strings.TrimPrefix(path, "/")

	// Ensure base URL doesn't have trailing slash
	baseURL := strings.TrimSuffix(s.config.BaseURL, "/")

	// Construct CDN URL
	return fmt.Sprintf("%s/%s", baseURL, path)
}

// GetURLs generates CDN URLs for multiple paths
func (s *CDNService) GetURLs(paths []string) map[string]string {
	result := make(map[string]string)
	for _, path := range paths {
		result[path] = s.GetURL(path)
	}
	return result
}

// GetAssetURL generates a CDN URL for a static asset
func (s *CDNService) GetAssetURL(assetType string, filename string) string {
	path := fmt.Sprintf("assets/%s/%s", assetType, filename)
	return s.GetURL(path)
}

// GetAudioURL generates a CDN URL for an audio file
func (s *CDNService) GetAudioURL(trackID string, filename string) string {
	path := fmt.Sprintf("audio/%s/%s", trackID, filename)
	return s.GetURL(path)
}

// GetHLSURL generates a CDN URL for an HLS stream
func (s *CDNService) GetHLSURL(trackID string, path string) string {
	fullPath := fmt.Sprintf("hls/%s/%s", trackID, path)
	return s.GetURL(fullPath)
}

// GetImageURL generates a CDN URL for an image
func (s *CDNService) GetImageURL(imageType string, filename string) string {
	path := fmt.Sprintf("images/%s/%s", imageType, filename)
	return s.GetURL(path)
}

// InvalidateCache invalidates CDN cache for given paths
func (s *CDNService) InvalidateCache(ctx context.Context, paths []string) error {
	if !s.config.Enabled {
		s.logger.Debug("CDN cache invalidation skipped (CDN disabled)")
		return nil
	}

	if len(paths) == 0 {
		return nil
	}

	switch s.config.Provider {
	case CDNProviderCloudFront:
		return s.invalidateCloudFront(ctx, paths)
	case CDNProviderCloudflare:
		return s.invalidateCloudflare(ctx, paths)
	case CDNProviderGeneric:
		// Generic CDN - just log, actual invalidation would need API integration
		s.logger.Info("CDN cache invalidation requested (generic provider)",
			zap.Strings("paths", paths),
		)
		return nil
	default:
		s.logger.Warn("CDN cache invalidation not supported for provider",
			zap.String("provider", string(s.config.Provider)),
		)
		return nil
	}
}

// invalidateCloudFront invalidates CloudFront cache
func (s *CDNService) invalidateCloudFront(ctx context.Context, paths []string) error {
	// Note: Full CloudFront invalidation would require AWS SDK
	// This is a placeholder implementation
	s.logger.Info("CloudFront cache invalidation requested",
		zap.String("distribution_id", s.config.DistributionID),
		zap.Strings("paths", paths),
	)

	// In a real implementation, this would:
	// 1. Create CloudFront invalidation request
	// 2. Use AWS SDK to submit invalidation
	// 3. Return invalidation ID for tracking

	return nil
}

// invalidateCloudflare invalidates Cloudflare cache
func (s *CDNService) invalidateCloudflare(ctx context.Context, paths []string) error {
	// Note: Full Cloudflare invalidation would require Cloudflare API
	// This is a placeholder implementation
	s.logger.Info("Cloudflare cache invalidation requested",
		zap.Strings("paths", paths),
	)

	// In a real implementation, this would:
	// 1. Call Cloudflare API to purge cache
	// 2. Handle API authentication
	// 3. Return purge result

	return nil
}

// IsEnabled returns whether CDN is enabled
func (s *CDNService) IsEnabled() bool {
	return s.config.Enabled && s.config.Provider != CDNProviderNone
}

// GetProvider returns the CDN provider type
func (s *CDNService) GetProvider() CDNProvider {
	return s.config.Provider
}

// GetBaseURL returns the CDN base URL
func (s *CDNService) GetBaseURL() string {
	return s.config.BaseURL
}

// GenerateSignedURL generates a signed URL for private content (if supported)
func (s *CDNService) GenerateSignedURL(path string, expiration time.Duration) (string, error) {
	if !s.config.Enabled {
		return s.GetURL(path), nil
	}

	switch s.config.Provider {
	case CDNProviderCloudFront:
		return s.generateCloudFrontSignedURL(path, expiration)
	case CDNProviderCloudflare:
		// Cloudflare doesn't support signed URLs in the same way
		// Return regular URL
		return s.GetURL(path), nil
	default:
		return s.GetURL(path), nil
	}
}

// generateCloudFrontSignedURL generates a CloudFront signed URL
func (s *CDNService) generateCloudFrontSignedURL(path string, expiration time.Duration) (string, error) {
	// Note: Full CloudFront signed URL generation would require AWS SDK
	// This is a placeholder implementation
	s.logger.Info("CloudFront signed URL generation requested",
		zap.String("path", path),
		zap.Duration("expiration", expiration),
	)

	// In a real implementation, this would:
	// 1. Use AWS SDK to generate signed URL
	// 2. Include expiration time
	// 3. Sign with private key

	// For now, return regular URL
	return s.GetURL(path), nil
}

// GetCacheHeaders returns appropriate cache headers for CDN
func (s *CDNService) GetCacheHeaders() map[string]string {
	headers := make(map[string]string)

	if !s.config.Enabled {
		headers["Cache-Control"] = "no-cache"
		return headers
	}

	switch s.config.Provider {
	case CDNProviderCloudFront:
		headers["Cache-Control"] = "public, max-age=31536000, immutable"
		headers["X-CDN-Provider"] = "cloudfront"
	case CDNProviderCloudflare:
		headers["Cache-Control"] = "public, max-age=31536000, immutable"
		headers["X-CDN-Provider"] = "cloudflare"
	default:
		headers["Cache-Control"] = "public, max-age=3600"
	}

	return headers
}

// BatchInvalidate invalidates multiple paths in batches (useful for rate limits)
func (s *CDNService) BatchInvalidate(ctx context.Context, paths []string, batchSize int) error {
	if batchSize <= 0 {
		batchSize = 10 // Default batch size
	}

	for i := 0; i < len(paths); i += batchSize {
		end := i + batchSize
		if end > len(paths) {
			end = len(paths)
		}

		batch := paths[i:end]
		if err := s.InvalidateCache(ctx, batch); err != nil {
			s.logger.Warn("Failed to invalidate cache batch",
				zap.Int("batch_start", i),
				zap.Int("batch_end", end),
				zap.Error(err),
			)
			// Continue with next batch even if one fails
		}

		// Small delay between batches to respect rate limits
		if end < len(paths) {
			time.Sleep(100 * time.Millisecond)
		}
	}

	return nil
}
