package routing import ( "context" "fmt" "trip-planner/internal/cache" "trip-planner/internal/yandex" ) // SearchCacheService handles caching and on-demand Yandex /search calls. type SearchCacheService struct { cache *cache.CacheAside yclient *yandex.Client } // NewSearchCacheService creates a new search cache service. func NewSearchCacheService(cacheStore cache.Cache, yclient *yandex.Client) *SearchCacheService { return &SearchCacheService{ cache: cache.NewCacheAside(cacheStore), yclient: yclient, } } // SearchWithCache performs a route search with caching support. // It uses the cache-aside pattern: try cache first, then Yandex API, then write back to cache. func (s *SearchCacheService) SearchWithCache(ctx context.Context, from, to, date string, opts SearchOptions) (*Itinerary, error) { // Generate cache key searchKey := cache.GetSearchKey(from, to, date) // Try to get from cache first fetchFunc := func() ([]byte, error) { // If we reach here, it's a cache miss - perform on-demand Yandex /search call return s.performYandexSearch(ctx, from, to, date, opts) } // Get or set from cache with appropriate TTL based on far-term flag isFarTerm := opts.FarTerm data, err := s.cache.GetSearch(ctx, searchKey, fetchFunc, isFarTerm) if err != nil { return nil, fmt.Errorf("search cache get/set: %w", err) } // Parse the itinerary from cached data (assuming JSON format) var result Itinerary if err := parseItineraryFromBytes(data, &result); err != nil { return nil, fmt.Errorf("failed to parse itinerary from cache: %w", err) } return &result, nil } // performYandexSearch makes the actual Yandex /search API call. func (s *SearchCacheService) performYandexSearch(ctx context.Context, from, to, date string, opts SearchOptions) ([]byte, error) { // Build query parameters for Yandex /search endpoint query := map[string]string{ "from": from, "to": to, "date": date, } // Execute the Yandex API request resp, err := s.yclient.Do(ctx, "GET", "/v3.0/search/", query) if err != nil { return nil, fmt.Errorf("yandex search failed: %w", err) } // Convert response to bytes for caching return convertResponseToBytes(resp) } // parseItineraryFromBytes parses an itinerary from byte data. func parseItineraryFromBytes(data []byte, result *Itinerary) error { // This is a placeholder - in real implementation, this would parse JSON // into the Itinerary struct if len(data) == 0 { return fmt.Errorf("empty data") } return nil } // convertResponseToBytes converts Yandex API response to bytes for caching. func convertResponseToBytes(resp *yandex.Response) ([]byte, error) { // This is a placeholder - in real implementation, this would serialize the response if resp == nil { return nil, fmt.Errorf("nil response") } return []byte(`{"search":{"from":"%s","to":"%s"},"segments":[]}`), nil }