package routing

import (
	"context"
	"encoding/json"
	"fmt"

	"trip-planner/internal/cache"
	"trip-planner/internal/metrics"
	"trip-planner/internal/yandex"
)

// SearchCacheService handles caching and on-demand Yandex /search calls.
type SearchCacheService struct {
	cache   *cache.CacheAside
	yclient *yandex.Client
	metrics *metrics.Metrics
}

// NewSearchCacheService creates a new search cache service.
func NewSearchCacheService(cacheStore cache.Cache, yclient *yandex.Client, m *metrics.Metrics) *SearchCacheService {
	return &SearchCacheService{
		cache:   cache.NewCacheAside(cacheStore, m),
		yclient: yclient,
		metrics: m,
	}
}

// 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) (*yandex.Response, 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 yandex.Response from cached data
	var result yandex.Response
	if err := json.Unmarshal(data, &result); err != nil {
		return nil, fmt.Errorf("failed to parse yandex response 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)
}

// convertResponseToBytes converts Yandex API response to bytes for caching.
func convertResponseToBytes(resp *yandex.Response) ([]byte, error) {
	if resp == nil {
		return nil, fmt.Errorf("nil response")
	}
	data, err := json.Marshal(resp)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal response: %w", err)
	}
	return data, nil
}
