package cache

import (
	"context"
	"encoding/json"
	"testing"
	"time"
)

// mockCacheStore is a mock implementation of Cache for testing
type mockCacheStore struct {
	data map[string][]byte
}

func (m *mockCacheStore) Get(ctx context.Context, key *CacheKey) ([]byte, error) {
	keyStr := key.Kind + ":" + key.Code
	if data, ok := m.data[keyStr]; ok {
		return data, nil
	}
	return nil, nil
}

func (m *mockCacheStore) Set(ctx context.Context, key *CacheKey, data []byte, ttl time.Duration) error {
	keyStr := key.Kind + ":" + key.Code
	m.data[keyStr] = data
	return nil
}

func (m *mockCacheStore) Exists(ctx context.Context, key *CacheKey) (bool, error) {
	keyStr := key.Kind + ":" + key.Code
	_, ok := m.data[keyStr]
	return ok, nil
}

func (m *mockCacheStore) Delete(ctx context.Context, key *CacheKey) error {
	keyStr := key.Kind + ":" + key.Code
	delete(m.data, keyStr)
	return nil
}

func (m *mockCacheStore) Increment(ctx context.Context, key *CacheKey) (int64, error) {
	return 0, nil
}

func (m *mockCacheStore) Decrement(ctx context.Context, key *CacheKey) (int64, error) {
	return 0, nil
}

func TestNewPreferences(t *testing.T) {
	mockStore := &mockCacheStore{data: make(map[string][]byte)}
	prefs := NewPreferences(mockStore)
	if prefs == nil {
		t.Error("expected Preferences to be created")
	}
	if prefs.store == nil {
		t.Error("expected store to be initialized")
	}
}

func TestPreferencesGetSavedCities(t *testing.T) {
	mockStore := &mockCacheStore{data: make(map[string][]byte)}
	prefs := NewPreferences(mockStore)

	// Test with no data
	cities, err := prefs.GetSavedCities(context.Background(), "user1")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if len(cities) != 0 {
		t.Errorf("expected 0 cities, got %d", len(cities))
	}

	// Test with data - the key is "prefs:saved_city:user1:user1"
	citiesData, _ := json.Marshal([]PreferenceSavedCity{
		{CityCode: "c1", Name: "Moscow"},
		{CityCode: "c2", Name: "St. Petersburg"},
	})
	mockStore.data["prefs:saved_city:user1:user1"] = citiesData

	cities, err = prefs.GetSavedCities(context.Background(), "user1")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if len(cities) != 2 {
		t.Errorf("expected 2 cities, got %d", len(cities))
	}
	if cities[0].CityCode != "c1" {
		t.Errorf("expected first city code 'c1', got '%s'", cities[0].CityCode)
	}
}

func TestPreferencesAddSavedCity(t *testing.T) {
	mockStore := &mockCacheStore{data: make(map[string][]byte)}
	prefs := NewPreferences(mockStore)

	// Add first city
	err := prefs.AddSavedCity(context.Background(), "user1", "c1", "Moscow")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}

	// Add second city
	err = prefs.AddSavedCity(context.Background(), "user1", "c2", "St. Petersburg")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}

	// Verify cities
	cities, err := prefs.GetSavedCities(context.Background(), "user1")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if len(cities) != 2 {
		t.Errorf("expected 2 cities, got %d", len(cities))
	}

	// Update existing city
	err = prefs.AddSavedCity(context.Background(), "user1", "c1", "Moscow Updated")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}

	cities, err = prefs.GetSavedCities(context.Background(), "user1")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if cities[0].Name != "Moscow Updated" {
		t.Errorf("expected city name 'Moscow Updated', got '%s'", cities[0].Name)
	}
}

func TestPreferencesRemoveSavedCity(t *testing.T) {
	mockStore := &mockCacheStore{data: make(map[string][]byte)}
	prefs := NewPreferences(mockStore)

	// Add cities
	prefs.AddSavedCity(context.Background(), "user1", "c1", "Moscow")
	prefs.AddSavedCity(context.Background(), "user1", "c2", "St. Petersburg")

	// Remove one city
	err := prefs.RemoveSavedCity(context.Background(), "user1", "c1")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}

	cities, err := prefs.GetSavedCities(context.Background(), "user1")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if len(cities) != 1 {
		t.Errorf("expected 1 city, got %d", len(cities))
	}
	if cities[0].CityCode != "c2" {
		t.Errorf("expected city code 'c2', got '%s'", cities[0].CityCode)
	}

	// Remove all cities
	err = prefs.RemoveSavedCity(context.Background(), "user1", "c2")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}

	cities, err = prefs.GetSavedCities(context.Background(), "user1")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if len(cities) != 0 {
		t.Errorf("expected 0 cities, got %d", len(cities))
	}
}

func TestPreferencesGetSearchHistory(t *testing.T) {
	mockStore := &mockCacheStore{data: make(map[string][]byte)}
	prefs := NewPreferences(mockStore)

	// Test with no data
	history, err := prefs.GetSearchHistory(context.Background(), "user1")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if len(history) != 0 {
		t.Errorf("expected 0 history entries, got %d", len(history))
	}

	// Test with data - the key is "prefs:search_history:user1:user1"
	historyData, _ := json.Marshal([]PreferenceSearchHistory{
		{Query: "c1→c2", FromCity: "c1", ToCity: "c2", Date: "2026-08-15", CreatedAt: time.Now().Unix()},
	})
	mockStore.data["prefs:search_history:user1:user1"] = historyData

	history, err = prefs.GetSearchHistory(context.Background(), "user1")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if len(history) != 1 {
		t.Errorf("expected 1 history entry, got %d", len(history))
	}
	if history[0].FromCity != "c1" {
		t.Errorf("expected from_city 'c1', got '%s'", history[0].FromCity)
	}
}

func TestPreferencesAddSearchHistory(t *testing.T) {
	mockStore := &mockCacheStore{data: make(map[string][]byte)}
	prefs := NewPreferences(mockStore)

	// Add search history
	err := prefs.AddSearchHistory(context.Background(), "user1", "c1", "c2", "2026-08-15")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}

	history, err := prefs.GetSearchHistory(context.Background(), "user1")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if len(history) != 1 {
		t.Errorf("expected 1 history entry, got %d", len(history))
	}
	if history[0].Query != "c1→c2" {
		t.Errorf("expected query 'c1→c2', got '%s'", history[0].Query)
	}
}

func TestPreferencesRemoveOldSearchHistory(t *testing.T) {
	mockStore := &mockCacheStore{data: make(map[string][]byte)}
	prefs := NewPreferences(mockStore)

	now := time.Now().Unix()

	// Add history with mixed ages
	history := []PreferenceSearchHistory{
		{Query: "old", FromCity: "c1", ToCity: "c2", Date: "2026-01-01", CreatedAt: now - 100},
		{Query: "new", FromCity: "c3", ToCity: "c4", Date: "2026-08-15", CreatedAt: now - 10},
	}
	historyData, _ := json.Marshal(history)
	mockStore.data["prefs:search_history:user1:user1"] = historyData

	// Remove old entries (keep only last 50 seconds)
	err := prefs.RemoveOldSearchHistory(context.Background(), "user1", 50)
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}

	history, err = prefs.GetSearchHistory(context.Background(), "user1")
	if err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if len(history) != 1 {
		t.Errorf("expected 1 history entry, got %d", len(history))
	}
	if history[0].Query != "new" {
		t.Errorf("expected query 'new', got '%s'", history[0].Query)
	}
}
