package cache

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

// PreferenceSavedCity represents a user's saved city preference.
type PreferenceSavedCity struct {
	CityCode string `json:"city_code"`
	Name     string `json:"name"`
}

// PreferenceSearchHistory represents a user's search history entry.
type PreferenceSearchHistory struct {
	Query     string `json:"query"`
	FromCity  string `json:"from_city"`
	ToCity    string `json:"to_city"`
	Date      string `json:"date"`
	CreatedAt int64  `json:"created_at"`
}

// Preferences represents user preferences storage.
// It provides methods for managing saved cities and search history.
type Preferences struct {
	store Cache
}

// NewPreferences creates a new Preferences instance with the given cache store.
func NewPreferences(store Cache) *Preferences {
	return &Preferences{store: store}
}

// GetSavedCities returns the user's saved cities.
func (p *Preferences) GetSavedCities(ctx context.Context, userID string) ([]PreferenceSavedCity, error) {
	data, err := p.store.Get(ctx, &CacheKey{
		Kind:    "prefs:saved_city:" + userID,
		Code:    userID,
		From:    "",
		To:      "",
		Date:    "",
		Request: "",
	})
	if err != nil {
		return nil, err
	}

	if data == nil {
		return []PreferenceSavedCity{}, nil
	}

	var cities []PreferenceSavedCity
	if err := json.Unmarshal(data, &cities); err != nil {
		return nil, err
	}
	return cities, nil
}

// AddSavedCity adds a city to the user's saved cities.
func (p *Preferences) AddSavedCity(ctx context.Context, userID, cityCode, cityName string) error {

	// Load existing cities
	cities, err := p.GetSavedCities(ctx, userID)
	if err != nil {
		return err
	}

	// Check if city already exists
	exists := false
	for i, c := range cities {
		if c.CityCode == cityCode {
			cities[i].Name = cityName
			exists = true
			break
		}
	}

	if !exists {
		// Add new city
		cities = append(cities, PreferenceSavedCity{
			CityCode: cityCode,
			Name:     cityName,
		})
	}

	// Store back to cache
	data, err := json.Marshal(cities)
	if err != nil {
		return err
	}

	cacheKey := &CacheKey{
		Kind:    "prefs:saved_city:" + userID,
		Code:    userID,
		From:    "",
		To:      "",
		Date:    "",
		Request: "",
	}
	if err := p.store.Set(ctx, cacheKey, data, PreferenceTTL); err != nil {
		return err
	}

	return nil
}

// RemoveSavedCity removes a city from the user's saved cities.
func (p *Preferences) RemoveSavedCity(ctx context.Context, userID, cityCode string) error {
	// Load existing cities
	cities, err := p.GetSavedCities(ctx, userID)
	if err != nil {
		return err
	}

	// Remove the city
	var result []PreferenceSavedCity
	for _, c := range cities {
		if c.CityCode != cityCode {
			result = append(result, c)
		}
	}

	if len(result) == 0 {
		// If no cities left, delete the key
		key := &CacheKey{
			Kind: "prefs:saved_city:" + userID,
			Code: userID,
		}
		return p.store.Delete(ctx, key)
	}

	// Store back
	data, err := json.Marshal(result)
	if err != nil {
		return err
	}

	key := &CacheKey{
		Kind: "prefs:saved_city:" + userID,
		Code: userID,
	}
	return p.store.Set(ctx, key, data, PreferenceTTL)
}

// GetSearchHistory returns the user's search history.
func (p *Preferences) GetSearchHistory(ctx context.Context, userID string) ([]PreferenceSearchHistory, error) {
	key := &CacheKey{
		Kind:    "prefs:search_history:" + userID,
		Code:    userID,
		From:    "",
		To:      "",
		Date:    "",
		Request: "",
	}

	data, err := p.store.Get(ctx, key)
	if err != nil {
		return nil, err
	}

	if data == nil {
		return []PreferenceSearchHistory{}, nil
	}

	var history []PreferenceSearchHistory
	if err := json.Unmarshal(data, &history); err != nil {
		return nil, err
	}
	return history, nil
}

// AddSearchHistory adds a search to the user's history.
func (p *Preferences) AddSearchHistory(ctx context.Context, userID, fromCity, toCity, date string) error {
	key := &CacheKey{
		Kind:    "prefs:search_history:" + userID,
		Code:    userID,
		From:    "",
		To:      "",
		Date:    "",
		Request: "",
	}

	// Load existing history
	history, err := p.GetSearchHistory(ctx, userID)
	if err != nil {
		return err
	}

	// Add new entry at the beginning (most recent first)
	history = append([]PreferenceSearchHistory{
		{
			Query:     fromCity + "→" + toCity,
			FromCity:  fromCity,
			ToCity:    toCity,
			Date:      date,
			CreatedAt: time.Now().Unix(),
		},
	}, history...)

	// Keep only last 50 searches
	if len(history) > 50 {
		history = history[:50]
	}

	// Store back to cache
	data, err := json.Marshal(history)
	if err != nil {
		return err
	}

	if err := p.store.Set(ctx, key, data, PreferenceTTL); err != nil {
		return err
	}

	return nil
}

// RemoveOldSearchHistory removes search entries older than the given age.
func (p *Preferences) RemoveOldSearchHistory(ctx context.Context, userID string, maxAgeSeconds int64) error {
	key := &CacheKey{
		Kind:    "prefs:search_history:" + userID,
		Code:    userID,
		From:    "",
		To:      "",
		Date:    "",
		Request: "",
	}

	history, err := p.GetSearchHistory(ctx, userID)
	if err != nil {
		return err
	}

	// Filter out old entries
	var recent []PreferenceSearchHistory
	now := time.Now().Unix()
	for _, entry := range history {
		if entry.CreatedAt >= now-maxAgeSeconds {
			recent = append(recent, entry)
		}
	}

	if len(recent) == len(history) {
		// No entries removed
		return nil
	}

	// Store back
	data, err := json.Marshal(recent)
	if err != nil {
		return err
	}

	if err := p.store.Set(ctx, key, data, PreferenceTTL); err != nil {
		return err
	}

	return nil
}
