import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lunchpick/core/utils/category_mapper.dart';
import 'package:lunchpick/core/utils/app_logger.dart';
import 'package:lunchpick/domain/entities/restaurant.dart';
import 'package:lunchpick/domain/repositories/restaurant_repository.dart';
import 'package:lunchpick/presentation/providers/di_providers.dart';
import 'package:lunchpick/presentation/providers/location_provider.dart';
import 'package:lunchpick/core/utils/distance_calculator.dart';
import 'package:uuid/uuid.dart';

/// 맛집 목록 Provider
final restaurantListProvider = StreamProvider<List<Restaurant>>((ref) {
  final repository = ref.watch(restaurantRepositoryProvider);
  return repository.watchRestaurants();
});

/// 거리 정보를 포함한 맛집 목록 Provider (현재 위치 기반)
/// StreamProvider 의존으로 초기 이벤트를 놓치는 문제를 피하기 위해
/// 기존 리스트 스트림의 AsyncValue를 그대로 전달하며 정렬만 적용한다.
final sortedRestaurantsByDistanceProvider =
    Provider<AsyncValue<List<({Restaurant restaurant, double? distanceKm})>>>((
      ref,
    ) {
      final restaurantsAsync = ref.watch(restaurantListProvider);
      final positionAsync = ref.watch(currentLocationWithFallbackProvider);
      final position = positionAsync.maybeWhen(
        data: (pos) => pos,
        orElse: () => defaultPosition(),
      );

      AppLogger.debug(
        '[restaurant_list] position ready for sorting: '
        '${position.latitude}, ${position.longitude}',
      );

      return restaurantsAsync.whenData((restaurants) {
        AppLogger.debug(
          '[restaurant_list] incoming restaurants: ${restaurants.length}',
        );
        final sorted =
            restaurants.map<({Restaurant restaurant, double? distanceKm})>((r) {
              final distanceKm = DistanceCalculator.calculateDistance(
                lat1: position.latitude,
                lon1: position.longitude,
                lat2: r.latitude,
                lon2: r.longitude,
              );
              return (restaurant: r, distanceKm: distanceKm);
            }).toList()..sort(
              (a, b) => (a.distanceKm ?? double.infinity).compareTo(
                b.distanceKm ?? double.infinity,
              ),
            );
        AppLogger.debug(
          '[restaurant_list] sorted list emitted, first distanceKm: '
          '${sorted.isNotEmpty ? sorted.first.distanceKm?.toStringAsFixed(3) : 'none'}',
        );
        return sorted;
      });
    });

/// 특정 맛집 Provider
final restaurantProvider = FutureProvider.family<Restaurant?, String>((
  ref,
  id,
) async {
  final repository = ref.watch(restaurantRepositoryProvider);
  return repository.getRestaurantById(id);
});

/// 카테고리 목록 Provider (맛집 스트림을 구독해 즉시 갱신)
final categoriesProvider = StreamProvider<List<String>>((ref) {
  final restaurantsStream = ref.watch(restaurantListProvider.stream);
  return restaurantsStream.map((restaurants) {
    final categories = restaurants.map((r) => r.category).toSet().toList()
      ..sort();
    return categories;
  });
});

/// 세부 카테고리 목록 Provider
final subCategoriesProvider = StreamProvider<List<String>>((ref) {
  final restaurantsStream = ref.watch(restaurantListProvider.stream);
  return restaurantsStream.map((restaurants) {
    final subCategories =
        restaurants
            .map((r) => r.subCategory)
            .where((s) => s.isNotEmpty)
            .toSet()
            .toList()
          ..sort();
    return subCategories;
  });
});

/// 맛집 관리 StateNotifier
class RestaurantNotifier extends StateNotifier<AsyncValue<void>> {
  final RestaurantRepository _repository;

  RestaurantNotifier(this._repository) : super(const AsyncValue.data(null));

  /// 맛집 추가
  Future<void> addRestaurant({
    required String name,
    required String category,
    required String subCategory,
    String? description,
    String? phoneNumber,
    required String roadAddress,
    required String jibunAddress,
    required double latitude,
    required double longitude,
    required DataSource source,
  }) async {
    state = const AsyncValue.loading();

    try {
      final restaurant = Restaurant(
        id: const Uuid().v4(),
        name: name,
        category: category,
        subCategory: subCategory,
        description: description,
        phoneNumber: phoneNumber,
        roadAddress: roadAddress,
        jibunAddress: jibunAddress,
        latitude: latitude,
        longitude: longitude,
        source: source,
        createdAt: DateTime.now(),
        updatedAt: DateTime.now(),
      );

      await _repository.addRestaurant(restaurant);
      state = const AsyncValue.data(null);
    } catch (e, stack) {
      state = AsyncValue.error(e, stack);
    }
  }

  /// 맛집 수정
  Future<void> updateRestaurant(Restaurant restaurant) async {
    state = const AsyncValue.loading();

    try {
      final nextSource = restaurant.source == DataSource.PRESET
          ? DataSource.USER_INPUT
          : restaurant.source;
      await _repository.updateRestaurant(
        restaurant.copyWith(source: nextSource, updatedAt: DateTime.now()),
      );
      state = const AsyncValue.data(null);
    } catch (e, stack) {
      state = AsyncValue.error(e, stack);
    }
  }

  /// 맛집 삭제
  Future<void> deleteRestaurant(String id) async {
    state = const AsyncValue.loading();

    try {
      await _repository.deleteRestaurant(id);
      state = const AsyncValue.data(null);
    } catch (e, stack) {
      state = AsyncValue.error(e, stack);
    }
  }

  /// 마지막 방문일 업데이트
  Future<void> updateLastVisitDate(
    String restaurantId,
    DateTime visitDate,
  ) async {
    try {
      await _repository.updateLastVisitDate(restaurantId, visitDate);
    } catch (e, stack) {
      state = AsyncValue.error(e, stack);
    }
  }

  /// 네이버 지도 URL로부터 맛집 추가
  Future<Restaurant> addRestaurantFromUrl(String url) async {
    state = const AsyncValue.loading();

    try {
      final restaurant = await _repository.addRestaurantFromUrl(url);
      state = const AsyncValue.data(null);
      return restaurant;
    } catch (e, stack) {
      state = AsyncValue.error(e, stack);
      rethrow;
    }
  }

  /// 미리 생성된 Restaurant 객체를 직접 추가
  Future<void> addRestaurantDirect(Restaurant restaurant) async {
    state = const AsyncValue.loading();

    try {
      await _repository.addRestaurant(restaurant);
      state = const AsyncValue.data(null);
    } catch (e, stack) {
      state = AsyncValue.error(e, stack);
      rethrow;
    }
  }
}

/// RestaurantNotifier Provider
final restaurantNotifierProvider =
    StateNotifierProvider<RestaurantNotifier, AsyncValue<void>>((ref) {
      final repository = ref.watch(restaurantRepositoryProvider);
      return RestaurantNotifier(repository);
    });

/// 거리 내 맛집 Provider
final restaurantsWithinDistanceProvider =
    FutureProvider.family<
      List<Restaurant>,
      ({double latitude, double longitude, double maxDistance})
    >((ref, params) async {
      final repository = ref.watch(restaurantRepositoryProvider);
      return repository.getRestaurantsWithinDistance(
        userLatitude: params.latitude,
        userLongitude: params.longitude,
        maxDistanceInMeters: params.maxDistance,
      );
    });

/// n일 이내 방문하지 않은 맛집 Provider
final restaurantsNotVisitedInDaysProvider =
    FutureProvider.family<List<Restaurant>, int>((ref, days) async {
      final repository = ref.watch(restaurantRepositoryProvider);
      return repository.getRestaurantsNotVisitedInDays(days);
    });

/// 검색어로 맛집 검색 Provider
final searchRestaurantsProvider =
    FutureProvider.family<List<Restaurant>, String>((ref, query) async {
      final repository = ref.watch(restaurantRepositoryProvider);
      return repository.searchRestaurants(query);
    });

/// 카테고리별 맛집 Provider
final restaurantsByCategoryProvider =
    FutureProvider.family<List<Restaurant>, String>((ref, category) async {
      final repository = ref.watch(restaurantRepositoryProvider);
      return repository.getRestaurantsByCategory(category);
    });

/// 검색 쿼리 상태 Provider
final searchQueryProvider = StateProvider<String>((ref) => '');

/// 선택된 카테고리 상태 Provider
final selectedCategoryProvider = StateProvider<String?>((ref) => null);

/// 필터링된 맛집 목록 Provider (검색 + 카테고리)
final filteredRestaurantsProvider = Provider<AsyncValue<List<Restaurant>>>((
  ref,
) {
  final searchQuery = ref.watch(searchQueryProvider);
  final selectedCategory = ref.watch(selectedCategoryProvider);
  final restaurantsAsync = ref.watch(restaurantListProvider);

  return restaurantsAsync.whenData((restaurants) {
    var filtered = restaurants;

    // 검색 필터 적용
    if (searchQuery.isNotEmpty) {
      final lowercaseQuery = searchQuery.toLowerCase();
      filtered = filtered.where((restaurant) {
        return restaurant.name.toLowerCase().contains(lowercaseQuery) ||
            (restaurant.description?.toLowerCase().contains(lowercaseQuery) ??
                false) ||
            restaurant.category.toLowerCase().contains(lowercaseQuery);
      }).toList();
    }

    // 카테고리 필터 적용
    if (selectedCategory != null) {
      filtered = filtered.where((restaurant) {
        // 정확한 일치 또는 부분 일치 확인
        // restaurant.category가 "음식점>한식>백반/한정식" 형태일 때
        // selectedCategory가 "백반/한정식"이면 매칭
        return restaurant.category == selectedCategory ||
            restaurant.category.contains(selectedCategory) ||
            CategoryMapper.normalizeNaverCategory(
                  restaurant.category,
                  restaurant.subCategory,
                ) ==
                selectedCategory ||
            CategoryMapper.getDisplayName(restaurant.category) ==
                selectedCategory;
      }).toList();
    }

    return filtered;
  });
});
