package processor

import (
	"context"
	"sync"
	"time"

	"eslogad-be/internal/contract"
	"eslogad-be/internal/repository"
	"eslogad-be/internal/transformer"

	"github.com/google/uuid"
)

// CachedUserProcessor wraps UserProcessor with caching for frequently accessed users
type CachedUserProcessor struct {
	userRepo    *repository.UserRepositoryImpl
	profileRepo *repository.UserProfileRepository
	cache       map[uuid.UUID]*cacheEntry
	mu          sync.RWMutex
	ttl         time.Duration
}

type cacheEntry struct {
	user      *contract.UserResponse
	expiresAt time.Time
}

func NewCachedUserProcessor(userRepo *repository.UserRepositoryImpl, profileRepo *repository.UserProfileRepository, userRoleProc UserRoleProcessor) *CachedUserProcessor {
	return &CachedUserProcessor{
		userRepo:    userRepo,
		profileRepo: profileRepo,
		cache:       make(map[uuid.UUID]*cacheEntry),
		ttl:         5 * time.Minute, // Cache for 5 minutes
	}
}

func (p *CachedUserProcessor) GetUserByIDCached(ctx context.Context, id uuid.UUID) (*contract.UserResponse, error) {
	p.mu.RLock()
	if entry, exists := p.cache[id]; exists {
		if entry.expiresAt.After(time.Now()) {
			p.mu.RUnlock()
			return entry.user, nil
		}
	}
	p.mu.RUnlock()

	// Not in cache or expired, fetch from database using the light method
	user, err := p.userRepo.GetByIDLight(ctx, id)
	if err != nil {
		return nil, err
	}

	// Convert to contract response
	resp := &contract.UserResponse{
		ID:        user.ID,
		Email:     user.Email,
		Name:      user.Name,
		IsActive:  user.IsActive,
		CreatedAt: user.CreatedAt,
		UpdatedAt: user.UpdatedAt,
	}

	// Store in cache
	p.mu.Lock()
	p.cache[id] = &cacheEntry{
		user:      resp,
		expiresAt: time.Now().Add(p.ttl),
	}
	p.mu.Unlock()

	// Clean expired entries periodically
	go p.cleanExpiredEntries()

	return resp, nil
}

// GetUserByIDFull retrieves full user with all relationships - no caching
func (p *CachedUserProcessor) GetUserByIDFull(ctx context.Context, id uuid.UUID) (*contract.UserResponse, error) {
	user, err := p.userRepo.GetByID(ctx, id)
	if err != nil {
		return nil, err
	}

	resp := transformer.EntityToContract(user)
	if resp != nil {
		if roles, err := p.userRepo.GetRolesByUserID(ctx, resp.ID); err == nil {
			resp.Roles = transformer.RolesToContract(roles)
		}
	}
	return resp, nil
}

// InvalidateCache removes a user from cache
func (p *CachedUserProcessor) InvalidateCache(userID uuid.UUID) {
	p.mu.Lock()
	delete(p.cache, userID)
	p.mu.Unlock()
}

// cleanExpiredEntries removes expired cache entries
func (p *CachedUserProcessor) cleanExpiredEntries() {
	p.mu.Lock()
	defer p.mu.Unlock()

	now := time.Now()
	for id, entry := range p.cache {
		if entry.expiresAt.Before(now) {
			delete(p.cache, id)
		}
	}
}
