import 'dart:math';
import 'package:lunchpick/domain/entities/restaurant.dart';
import 'package:lunchpick/domain/entities/user_settings.dart';
import 'package:lunchpick/domain/entities/visit_record.dart';
import 'package:lunchpick/domain/entities/weather_info.dart';
import 'package:lunchpick/core/utils/distance_calculator.dart';

/// 추천 엔진 설정
class RecommendationConfig {
  final double userLatitude;
  final double userLongitude;
  final double maxDistance;
  final List<String> selectedCategories;
  final UserSettings userSettings;
  final WeatherInfo? weather;
  final DateTime currentTime;

  RecommendationConfig({
    required this.userLatitude,
    required this.userLongitude,
    required this.maxDistance,
    required this.selectedCategories,
    required this.userSettings,
    this.weather,
    DateTime? currentTime,
  }) : currentTime = currentTime ?? DateTime.now();
}

/// 추천 엔진 UseCase
class RecommendationEngine {
  final Random _random = Random();

  /// 추천 생성
  Future<Restaurant?> generateRecommendation({
    required List<Restaurant> allRestaurants,
    required List<VisitRecord> recentVisits,
    required RecommendationConfig config,
  }) async {
    // 1단계: 거리 필터링
    final restaurantsInRange = _filterByDistance(allRestaurants, config);
    if (restaurantsInRange.isEmpty) return null;

    // 2단계: 재방문 방지 필터링
    final eligibleRestaurants = _filterByRevisitPrevention(
      restaurantsInRange,
      recentVisits,
      config.userSettings.revisitPreventionDays,
    );
    if (eligibleRestaurants.isEmpty) return null;

    // 3단계: 카테고리 필터링
    final filteredByCategory = _filterByCategory(
      eligibleRestaurants,
      config.selectedCategories,
    );
    if (filteredByCategory.isEmpty) return null;

    // 4단계: 가중치 계산 및 선택
    return _selectWithWeights(filteredByCategory, config);
  }

  /// 거리 기반 필터링
  List<Restaurant> _filterByDistance(
    List<Restaurant> restaurants,
    RecommendationConfig config,
  ) {
    // 날씨에 따른 최대 거리 조정
    double effectiveMaxDistance = config.maxDistance;
    if (config.weather != null && config.weather!.current.isRainy) {
      // 비가 올 때는 거리를 70%로 줄임
      effectiveMaxDistance *= 0.7;
    }

    return restaurants.where((restaurant) {
      final distance = DistanceCalculator.calculateDistance(
        lat1: config.userLatitude,
        lon1: config.userLongitude,
        lat2: restaurant.latitude,
        lon2: restaurant.longitude,
      );
      return distance <= effectiveMaxDistance;
    }).toList();
  }

  /// 재방문 방지 필터링
  List<Restaurant> _filterByRevisitPrevention(
    List<Restaurant> restaurants,
    List<VisitRecord> recentVisits,
    int preventionDays,
  ) {
    final now = DateTime.now();
    final cutoffDate = now.subtract(Duration(days: preventionDays));

    // 최근 n일 내 방문한 식당 ID 수집
    final recentlyVisitedIds = recentVisits
        .where((visit) => visit.visitDate.isAfter(cutoffDate))
        .map((visit) => visit.restaurantId)
        .toSet();

    // 최근 방문하지 않은 식당만 필터링
    return restaurants.where((restaurant) {
      return !recentlyVisitedIds.contains(restaurant.id);
    }).toList();
  }

  /// 카테고리 필터링
  List<Restaurant> _filterByCategory(
    List<Restaurant> restaurants,
    List<String> selectedCategories,
  ) {
    if (selectedCategories.isEmpty) {
      return restaurants;
    }
    return restaurants.where((restaurant) {
      return selectedCategories.contains(restaurant.category);
    }).toList();
  }

  /// 가중치 기반 선택
  Restaurant? _selectWithWeights(
    List<Restaurant> restaurants,
    RecommendationConfig config,
  ) {
    if (restaurants.isEmpty) return null;

    // 각 식당에 대한 가중치 계산
    final weightedRestaurants = restaurants.map((restaurant) {
      double weight = 1.0;

      // 카테고리 가중치 적용
      final categoryWeight =
          config.userSettings.categoryWeights[restaurant.category];
      if (categoryWeight != null) {
        weight *= categoryWeight;
      }

      // 거리 가중치 적용 (가까울수록 높은 가중치)
      final distance = DistanceCalculator.calculateDistance(
        lat1: config.userLatitude,
        lon1: config.userLongitude,
        lat2: restaurant.latitude,
        lon2: restaurant.longitude,
      );
      final distanceWeight = 1.0 - (distance / config.maxDistance);
      weight *= (0.5 + distanceWeight * 0.5); // 50% ~ 100% 범위

      // 시간대별 가중치 적용
      weight *= _getTimeBasedWeight(restaurant, config.currentTime);

      // 날씨 기반 가중치 적용
      if (config.weather != null) {
        weight *= _getWeatherBasedWeight(restaurant, config.weather!);
      }

      return _WeightedRestaurant(restaurant, weight);
    }).toList();

    // 가중치 기반 랜덤 선택
    return _weightedRandomSelection(weightedRestaurants);
  }

  /// 시간대별 가중치 계산
  double _getTimeBasedWeight(Restaurant restaurant, DateTime currentTime) {
    final hour = currentTime.hour;

    // 아침 시간대 (7-10시)
    if (hour >= 7 && hour < 10) {
      if (restaurant.category == 'cafe' || restaurant.category == 'korean') {
        return 1.2;
      }
      if (restaurant.category == 'bar') {
        return 0.3;
      }
    }
    // 점심 시간대 (11-14시)
    else if (hour >= 11 && hour < 14) {
      if (restaurant.category == 'korean' ||
          restaurant.category == 'chinese' ||
          restaurant.category == 'japanese') {
        return 1.3;
      }
    }
    // 저녁 시간대 (17-21시)
    else if (hour >= 17 && hour < 21) {
      if (restaurant.category == 'bar' || restaurant.category == 'western') {
        return 1.2;
      }
    }
    // 늦은 저녁 (21시 이후)
    else if (hour >= 21) {
      if (restaurant.category == 'bar' || restaurant.category == 'fastfood') {
        return 1.3;
      }
      if (restaurant.category == 'cafe') {
        return 0.5;
      }
    }

    return 1.0;
  }

  /// 날씨 기반 가중치 계산
  double _getWeatherBasedWeight(Restaurant restaurant, WeatherInfo weather) {
    if (weather.current.isRainy) {
      // 비가 올 때는 가까운 식당 선호
      // 이미 거리 가중치에서 처리했으므로 여기서는 실내 카테고리 선호
      if (restaurant.category == 'cafe' || restaurant.category == 'fastfood') {
        return 1.2;
      }
    }

    // 더운 날씨 (25도 이상)
    if (weather.current.temperature >= 25) {
      if (restaurant.category == 'cafe' || restaurant.category == 'japanese') {
        return 1.1;
      }
    }

    // 추운 날씨 (10도 이하)
    if (weather.current.temperature <= 10) {
      if (restaurant.category == 'korean' || restaurant.category == 'chinese') {
        return 1.2;
      }
    }

    return 1.0;
  }

  /// 가중치 기반 랜덤 선택
  Restaurant? _weightedRandomSelection(
    List<_WeightedRestaurant> weightedRestaurants,
  ) {
    if (weightedRestaurants.isEmpty) return null;

    // 전체 가중치 합계 계산
    final totalWeight = weightedRestaurants.fold<double>(
      0,
      (sum, item) => sum + item.weight,
    );

    // 랜덤 값 생성
    final randomValue = _random.nextDouble() * totalWeight;

    // 누적 가중치로 선택
    double cumulativeWeight = 0;
    for (final weightedRestaurant in weightedRestaurants) {
      cumulativeWeight += weightedRestaurant.weight;
      if (randomValue <= cumulativeWeight) {
        return weightedRestaurant.restaurant;
      }
    }

    // 예외 처리 (여기에 도달하면 안됨)
    return weightedRestaurants.last.restaurant;
  }
}

/// 가중치가 적용된 식당 모델
class _WeightedRestaurant {
  final Restaurant restaurant;
  final double weight;

  _WeightedRestaurant(this.restaurant, this.weight);
}
