package services

import (
	"astrology-api/dto"
	"astrology-api/repositories"
	"sync"
	"time"
)

type AstrologerService interface {
	GetAstrologers(req dto.AstrologerListRequest) ([]dto.AstrologerResponse, error)

	SearchAstrologers(req dto.SearchAstrologerRequest) ([]dto.SearchAstrologerResponse, error)

	GetAstrologerByID(req dto.GetAstrologerByIDRequest) (*dto.GetAstrologerByIDResponse, error)

	GetTopAstrologers(limit int) ([]dto.TopAstrologerResponse, error)
}

//////////////////////////////////////////////////////////////
// Top astrologers (public, website home page)
//////////////////////////////////////////////////////////////

const (
	defaultTopAstrologers = 3
	maxTopAstrologers     = 10

	// The home page is public and hit on every visit; the ranking moves
	// slowly, so it is cached briefly. Online/busy status can lag by this
	// much.
	topAstrologersCacheTTL = 2 * time.Minute
)

var topAstrologersCache = struct {
	sync.Mutex
	at   time.Time
	list []dto.TopAstrologerResponse
}{}

// GetTopAstrologers caches the top maxTopAstrologers once and slices it for
// any smaller limit.
func (s *astrologerService) GetTopAstrologers(limit int) ([]dto.TopAstrologerResponse, error) {

	if limit <= 0 {
		limit = defaultTopAstrologers
	}

	if limit > maxTopAstrologers {
		limit = maxTopAstrologers
	}

	topAstrologersCache.Lock()
	defer topAstrologersCache.Unlock()

	if topAstrologersCache.list == nil || time.Since(topAstrologersCache.at) > topAstrologersCacheTTL {

		list, err := s.repository.GetTopAstrologers(maxTopAstrologers)

		if err != nil {
			return nil, err
		}

		topAstrologersCache.list = list
		topAstrologersCache.at = time.Now()
	}

	list := topAstrologersCache.list

	if len(list) > limit {
		list = list[:limit]
	}

	return list, nil
}

type astrologerService struct {
	repository repositories.AstrologerRepository
}

func NewAstrologerService(
	repository repositories.AstrologerRepository,
) AstrologerService {

	return &astrologerService{
		repository: repository,
	}
}

func (s *astrologerService) GetAstrologers(
	req dto.AstrologerListRequest,
) ([]dto.AstrologerResponse, error) {

	return s.repository.GetAstrologers(req)
}

func (s *astrologerService) SearchAstrologers(
	req dto.SearchAstrologerRequest,
) ([]dto.SearchAstrologerResponse, error) {

	return s.repository.SearchAstrologers(req)
}

func (s *astrologerService) GetAstrologerByID(req dto.GetAstrologerByIDRequest) (*dto.GetAstrologerByIDResponse, error) {

	return s.repository.GetAstrologerByID(req)
}
