import 'dart:math';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lunchpick/domain/entities/recommendation_record.dart';
import 'package:lunchpick/domain/entities/restaurant.dart';
import 'package:lunchpick/domain/repositories/recommendation_repository.dart';
import 'package:lunchpick/domain/usecases/recommendation_engine.dart';
import 'package:lunchpick/presentation/providers/di_providers.dart';
import 'package:lunchpick/presentation/providers/restaurant_provider.dart';
import 'package:lunchpick/presentation/providers/settings_provider.dart'
    hide currentLocationProvider, locationPermissionProvider;
import 'package:lunchpick/presentation/providers/weather_provider.dart';
import 'package:lunchpick/presentation/providers/location_provider.dart';
import 'package:lunchpick/presentation/providers/visit_provider.dart';
import 'package:uuid/uuid.dart';

/// 추천 기록 목록 Provider
final recommendationRecordsProvider =
    StreamProvider<List<RecommendationRecord>>((ref) {
      final repository = ref.watch(recommendationRepositoryProvider);
      return repository.watchRecommendationRecords();
    });

/// 오늘의 추천 횟수 Provider
final todayRecommendationCountProvider = FutureProvider<int>((ref) async {
  final repository = ref.watch(recommendationRepositoryProvider);
  return repository.getTodayRecommendationCount();
});

Future<bool> _isScreenshotModeEnabled(Ref ref) async {
  try {
    return await ref.read(screenshotModeEnabledProvider.future);
  } catch (_) {
    return false;
  }
}

Restaurant _buildScreenshotRestaurant() {
  final now = DateTime.now();
  final random = Random();
  final templates = [
    (
      id: 'screenshot-basil-bistro',
      name: 'Basil Breeze Bistro',
      category: '양식',
      subCategory: '파스타 · 그릴',
      description: '바질 향이 가득한 파스타와 스테이크를 즐길 수 있는 다이닝.',
      phoneNumber: '02-1234-5678',
      roadAddress: '서울 중구 세종대로 110',
      jibunAddress: '서울 중구 태평로1가 31',
      latitude: 37.5665,
      longitude: 126.9780,
    ),
    (
      id: 'screenshot-komorebi-sushi',
      name: 'Komorebi Sushi',
      category: '일식',
      subCategory: '스시 · 사시미',
      description: '제철 재료로 선보이는 오마카세 콘셉트 스시 바.',
      phoneNumber: '02-2468-1357',
      roadAddress: '서울 강남구 테헤란로 311',
      jibunAddress: '서울 강남구 역삼동 647-9',
      latitude: 37.5009,
      longitude: 127.0365,
    ),
    (
      id: 'screenshot-brunch-lab',
      name: 'Sunny Brunch Lab',
      category: '카페/디저트',
      subCategory: '브런치 · 디저트',
      description: '스크램블 에그와 시그니처 라떼가 인기인 브런치 카페.',
      phoneNumber: '02-9753-8642',
      roadAddress: '서울 마포구 독막로 12길 5',
      jibunAddress: '서울 마포구 합정동 374-6',
      latitude: 37.5509,
      longitude: 126.9143,
    ),
  ];

  final template = templates[random.nextInt(templates.length)];

  return Restaurant(
    id: '${template.id}-${now.millisecondsSinceEpoch}',
    name: template.name,
    category: template.category,
    subCategory: template.subCategory,
    description: template.description,
    phoneNumber: template.phoneNumber,
    roadAddress: template.roadAddress,
    jibunAddress: template.jibunAddress,
    latitude: template.latitude,
    longitude: template.longitude,
    source: DataSource.PRESET,
    createdAt: now,
    updatedAt: now,
    needsAddressVerification: false,
  );
}

/// 추천 설정 모델
class RecommendationSettings {
  final int daysToExclude;
  final int maxDistanceRainy;
  final int maxDistanceNormal;
  final List<String> selectedCategories;

  RecommendationSettings({
    required this.daysToExclude,
    required this.maxDistanceRainy,
    required this.maxDistanceNormal,
    required this.selectedCategories,
  });
}

/// 추천 관리 StateNotifier
class RecommendationNotifier extends StateNotifier<AsyncValue<Restaurant?>> {
  final RecommendationRepository _repository;
  final Ref _ref;
  final RecommendationEngine _recommendationEngine = RecommendationEngine();

  RecommendationNotifier(this._repository, this._ref)
    : super(const AsyncValue.data(null));

  /// 랜덤 추천 실행
  Future<Restaurant?> getRandomRecommendation({
    required double maxDistance,
    required List<String> selectedCategories,
    List<String> excludedRestaurantIds = const [],
    bool shouldSaveRecord = true,
  }) async {
    state = const AsyncValue.loading();

    try {
      final screenshotModeEnabled = await _isScreenshotModeEnabled(_ref);
      if (screenshotModeEnabled) {
        final mock = _buildScreenshotRestaurant();
        state = AsyncValue.data(mock);
        return mock;
      }

      final selectedRestaurant = await _generateCandidate(
        maxDistance: maxDistance,
        selectedCategories: selectedCategories,
        excludedRestaurantIds: excludedRestaurantIds,
      );

      if (selectedRestaurant == null) {
        state = const AsyncValue.data(null);
        return null;
      }

      if (shouldSaveRecord) {
        await saveRecommendationRecord(selectedRestaurant);
      }

      state = AsyncValue.data(selectedRestaurant);
      return selectedRestaurant;
    } catch (e, stack) {
      state = AsyncValue.error(e, stack);
      return null;
    }
  }

  Future<Restaurant?> _generateCandidate({
    required double maxDistance,
    required List<String> selectedCategories,
    List<String> excludedRestaurantIds = const [],
  }) async {
    // 현재 위치 가져오기
    final location = await _ref.read(currentLocationProvider.future);
    if (location == null) {
      throw Exception('위치 정보를 가져올 수 없습니다');
    }

    // 날씨 정보 가져오기
    final weather = await _ref.read(weatherProvider.future);

    // 사용자 설정 가져오기
    final userSettings = await _ref.read(userSettingsProvider.future);

    // 모든 식당 가져오기
    final allRestaurants = await _ref.read(restaurantListProvider.future);

    // 방문 기록 가져오기
    final allVisitRecords = await _ref.read(visitRecordsProvider.future);

    // 제외된 식당 제거
    final availableRestaurants = excludedRestaurantIds.isEmpty
        ? allRestaurants
        : allRestaurants
              .where(
                (restaurant) => !excludedRestaurantIds.contains(restaurant.id),
              )
              .toList();

    if (availableRestaurants.isEmpty) {
      return null;
    }

    // 추천 설정 구성
    final config = RecommendationConfig(
      userLatitude: location.latitude,
      userLongitude: location.longitude,
      maxDistance: maxDistance / 1000, // 미터 입력을 km 단위로 변환
      selectedCategories: selectedCategories,
      userSettings: userSettings,
      weather: weather,
    );

    // 추천 엔진 사용
    return _recommendationEngine.generateRecommendation(
      allRestaurants: availableRestaurants,
      recentVisits: allVisitRecords,
      config: config,
    );
  }

  /// 추천 기록 저장
  Future<RecommendationRecord> saveRecommendationRecord(
    Restaurant restaurant, {
    DateTime? recommendationTime,
    bool visited = false,
  }) async {
    final screenshotModeEnabled = await _isScreenshotModeEnabled(_ref);
    final now = DateTime.now();

    if (screenshotModeEnabled) {
      return RecommendationRecord(
        id: 'screenshot-${now.millisecondsSinceEpoch}',
        restaurantId: restaurant.id,
        recommendationDate: recommendationTime ?? now,
        visited: visited,
        createdAt: now,
      );
    }

    final record = RecommendationRecord(
      id: const Uuid().v4(),
      restaurantId: restaurant.id,
      recommendationDate: recommendationTime ?? now,
      visited: visited,
      createdAt: now,
    );

    await _repository.addRecommendationRecord(record);
    return record;
  }

  /// 추천 후 방문 확인
  Future<void> confirmVisit(String recommendationId) async {
    try {
      await _repository.markAsVisited(recommendationId);

      // 방문 기록도 생성
      final recommendations = await _ref.read(
        recommendationRecordsProvider.future,
      );
      final recommendation = recommendations.firstWhere(
        (r) => r.id == recommendationId,
      );

      final visitNotifier = _ref.read(visitNotifierProvider.notifier);
      await visitNotifier.createVisitFromRecommendation(
        restaurantId: recommendation.restaurantId,
        recommendationTime: recommendation.recommendationDate,
        isConfirmed: true,
      );

      // 방문 기록을 만들었으므로 추천 기록은 숨김 처리
      await _repository.deleteRecommendationRecord(recommendationId);
    } catch (e, stack) {
      state = AsyncValue.error(e, stack);
    }
  }

  /// 추천 기록 삭제
  Future<void> deleteRecommendation(String id) async {
    try {
      await _repository.deleteRecommendationRecord(id);
    } catch (e, stack) {
      state = AsyncValue.error(e, stack);
    }
  }
}

/// RecommendationNotifier Provider
final recommendationNotifierProvider =
    StateNotifierProvider<RecommendationNotifier, AsyncValue<Restaurant?>>((
      ref,
    ) {
      final repository = ref.watch(recommendationRepositoryProvider);
      return RecommendationNotifier(repository, ref);
    });

/// 월별 추천 통계 Provider
final monthlyRecommendationStatsProvider =
    FutureProvider.family<Map<String, int>, ({int year, int month})>((
      ref,
      params,
    ) async {
      final repository = ref.watch(recommendationRepositoryProvider);
      return repository.getMonthlyRecommendationStats(
        params.year,
        params.month,
      );
    });

/// 추천 상태 관리 (다시 추천 기능 포함)
class RecommendationState {
  final Restaurant? currentRecommendation;
  final List<Restaurant> excludedRestaurants;
  final bool isLoading;
  final String? error;

  const RecommendationState({
    this.currentRecommendation,
    this.excludedRestaurants = const [],
    this.isLoading = false,
    this.error,
  });

  RecommendationState copyWith({
    Restaurant? currentRecommendation,
    List<Restaurant>? excludedRestaurants,
    bool? isLoading,
    String? error,
  }) {
    return RecommendationState(
      currentRecommendation:
          currentRecommendation ?? this.currentRecommendation,
      excludedRestaurants: excludedRestaurants ?? this.excludedRestaurants,
      isLoading: isLoading ?? this.isLoading,
      error: error,
    );
  }
}

/// 향상된 추천 StateNotifier (다시 추천 기능 포함)
class EnhancedRecommendationNotifier
    extends StateNotifier<RecommendationState> {
  final Ref _ref;
  final RecommendationEngine _recommendationEngine = RecommendationEngine();

  EnhancedRecommendationNotifier(this._ref)
    : super(const RecommendationState());

  /// 다시 추천 (현재 추천 제외)
  Future<void> rerollRecommendation() async {
    if (state.currentRecommendation == null) return;

    if (await _isScreenshotModeEnabled(_ref)) {
      await generateRecommendation();
      return;
    }

    // 현재 추천을 제외 목록에 추가
    final excluded = [
      ...state.excludedRestaurants,
      state.currentRecommendation!,
    ];
    state = state.copyWith(excludedRestaurants: excluded);

    // 다시 추천 생성 (제외 목록 적용)
    await generateRecommendation(excludedRestaurants: excluded);
  }

  /// 추천 생성 (새로운 추천 엔진 활용)
  Future<void> generateRecommendation({
    List<Restaurant>? excludedRestaurants,
  }) async {
    state = state.copyWith(isLoading: true);

    try {
      if (await _isScreenshotModeEnabled(_ref)) {
        final mock = _buildScreenshotRestaurant();
        state = state.copyWith(
          currentRecommendation: mock,
          excludedRestaurants: const [],
          isLoading: false,
          error: null,
        );
        return;
      }

      // 현재 위치 가져오기
      final location = await _ref.read(currentLocationProvider.future);
      if (location == null) {
        state = state.copyWith(error: '위치 정보를 가져올 수 없습니다', isLoading: false);
        return;
      }

      // 필요한 데이터 가져오기
      final weather = await _ref.read(weatherProvider.future);
      final userSettings = await _ref.read(userSettingsProvider.future);
      final allRestaurants = await _ref.read(restaurantListProvider.future);
      final allVisitRecords = await _ref.read(visitRecordsProvider.future);
      final maxDistanceNormal = await _ref.read(
        maxDistanceNormalProvider.future,
      );
      final selectedCategory = _ref.read(selectedCategoryProvider);
      final categories = selectedCategory != null
          ? [selectedCategory]
          : <String>[];

      // 제외 리스트 포함한 식당 필터링
      final availableRestaurants = excludedRestaurants != null
          ? allRestaurants
                .where((r) => !excludedRestaurants.any((ex) => ex.id == r.id))
                .toList()
          : allRestaurants;

      // 추천 설정 구성
      final config = RecommendationConfig(
        userLatitude: location.latitude,
        userLongitude: location.longitude,
        maxDistance: maxDistanceNormal.toDouble() / 1000, // 미터 입력을 km 단위로 변환
        selectedCategories: categories,
        userSettings: userSettings,
        weather: weather,
      );

      // 추천 엔진 사용
      final selectedRestaurant = await _recommendationEngine
          .generateRecommendation(
            allRestaurants: availableRestaurants,
            recentVisits: allVisitRecords,
            config: config,
          );

      if (selectedRestaurant != null) {
        // 추천 기록 저장
        final record = RecommendationRecord(
          id: const Uuid().v4(),
          restaurantId: selectedRestaurant.id,
          recommendationDate: DateTime.now(),
          visited: false,
          createdAt: DateTime.now(),
        );

        final repository = _ref.read(recommendationRepositoryProvider);
        await repository.addRecommendationRecord(record);

        state = state.copyWith(
          currentRecommendation: selectedRestaurant,
          isLoading: false,
        );
      } else {
        state = state.copyWith(error: '조건에 맞는 맛집이 없습니다', isLoading: false);
      }
    } catch (e) {
      state = state.copyWith(error: e.toString(), isLoading: false);
    }
  }

  /// 추천 초기화
  void resetRecommendation() {
    state = const RecommendationState();
  }
}

/// 향상된 추천 Provider
final enhancedRecommendationProvider =
    StateNotifierProvider<EnhancedRecommendationNotifier, RecommendationState>((
      ref,
    ) {
      return EnhancedRecommendationNotifier(ref);
    });

/// 추천 가능한 맛집 수 Provider
final recommendableRestaurantsCountProvider = FutureProvider<int>((ref) async {
  final daysToExclude = await ref.watch(daysToExcludeProvider.future);
  final recentlyVisited = await ref.watch(
    restaurantsNotVisitedInDaysProvider(daysToExclude).future,
  );

  return recentlyVisited.length;
});

/// 카테고리별 추천 통계 Provider
final recommendationStatsByCategoryProvider = FutureProvider<Map<String, int>>((
  ref,
) async {
  final records = await ref.watch(recommendationRecordsProvider.future);

  final stats = <String, int>{};
  for (final record in records) {
    final restaurant = await ref.watch(
      restaurantProvider(record.restaurantId).future,
    );
    if (restaurant != null) {
      stats[restaurant.category] = (stats[restaurant.category] ?? 0) + 1;
    }
  }

  return stats;
});

/// 추천 성공률 Provider
final recommendationSuccessRateProvider = FutureProvider<double>((ref) async {
  final records = await ref.watch(recommendationRecordsProvider.future);
  if (records.isEmpty) return 0.0;

  final visitedCount = records.where((r) => r.visited).length;
  return (visitedCount / records.length) * 100;
});

/// 가장 많이 추천된 맛집 Top 5 Provider
final topRecommendedRestaurantsProvider =
    FutureProvider<List<({String restaurantId, int count})>>((ref) async {
      final records = await ref.watch(recommendationRecordsProvider.future);

      final counts = <String, int>{};
      for (final record in records) {
        counts[record.restaurantId] = (counts[record.restaurantId] ?? 0) + 1;
      }

      final sorted = counts.entries.toList()
        ..sort((a, b) => b.value.compareTo(a.value));

      return sorted
          .take(5)
          .map((e) => (restaurantId: e.key, count: e.value))
          .toList();
    });
