import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lunchpick/domain/entities/visit_record.dart';
import 'package:lunchpick/domain/repositories/visit_repository.dart';
import 'package:lunchpick/presentation/providers/di_providers.dart';
import 'package:lunchpick/presentation/providers/restaurant_provider.dart';
import 'package:uuid/uuid.dart';

/// 방문 기록 목록 Provider
final visitRecordsProvider = StreamProvider<List<VisitRecord>>((ref) {
  final repository = ref.watch(visitRepositoryProvider);
  return repository.watchVisitRecords();
});

/// 날짜별 방문 기록 Provider
final visitRecordsByDateProvider =
    FutureProvider.family<List<VisitRecord>, DateTime>((ref, date) async {
      final repository = ref.watch(visitRepositoryProvider);
      return repository.getVisitRecordsByDate(date);
    });

/// 맛집별 방문 기록 Provider
final visitRecordsByRestaurantProvider =
    FutureProvider.family<List<VisitRecord>, String>((ref, restaurantId) async {
      final repository = ref.watch(visitRepositoryProvider);
      return repository.getVisitRecordsByRestaurantId(restaurantId);
    });

/// 월별 방문 통계 Provider
final monthlyVisitStatsProvider =
    FutureProvider.family<Map<String, int>, ({int year, int month})>((
      ref,
      params,
    ) async {
      final repository = ref.watch(visitRepositoryProvider);
      return repository.getMonthlyVisitStats(params.year, params.month);
    });

/// 월별 카테고리별 방문 통계 Provider
final monthlyCategoryVisitStatsProvider =
    FutureProvider.family<Map<String, int>, ({int year, int month})>((
      ref,
      params,
    ) async {
      final repository = ref.watch(visitRepositoryProvider);
      final restaurants = await ref.watch(restaurantListProvider.future);

      final records = await repository.getVisitRecordsByDateRange(
        startDate: DateTime(params.year, params.month, 1),
        endDate: DateTime(params.year, params.month + 1, 0),
      );

      final categoryCount = <String, int>{};
      for (final record in records) {
        final restaurant = restaurants
            .where((r) => r.id == record.restaurantId)
            .firstOrNull;
        if (restaurant == null) continue;

        categoryCount[restaurant.category] =
            (categoryCount[restaurant.category] ?? 0) + 1;
      }

      return categoryCount;
    });

/// 방문 기록 관리 StateNotifier
class VisitNotifier extends StateNotifier<AsyncValue<void>> {
  final VisitRepository _repository;
  final Ref _ref;

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

  /// 방문 기록 추가
  Future<void> addVisitRecord({
    required String restaurantId,
    required DateTime visitDate,
    bool isConfirmed = false,
  }) async {
    state = const AsyncValue.loading();

    try {
      final visitRecord = VisitRecord(
        id: const Uuid().v4(),
        restaurantId: restaurantId,
        visitDate: visitDate,
        isConfirmed: isConfirmed,
        createdAt: DateTime.now(),
      );

      await _repository.addVisitRecord(visitRecord);

      // 맛집의 마지막 방문일도 업데이트
      final restaurantNotifier = _ref.read(restaurantNotifierProvider.notifier);
      await restaurantNotifier.updateLastVisitDate(restaurantId, visitDate);

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

  /// 방문 확인
  Future<void> confirmVisit(String visitRecordId) async {
    state = const AsyncValue.loading();

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

  /// 방문 기록 삭제
  Future<void> deleteVisitRecord(String id) async {
    state = const AsyncValue.loading();

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

  /// 추천 후 자동 방문 기록 생성
  Future<void> createVisitFromRecommendation({
    required String restaurantId,
    required DateTime recommendationTime,
    bool isConfirmed = false,
  }) async {
    // 추천 확인 시점으로 방문 시간을 기록
    final visitTime = DateTime.now();

    await addVisitRecord(
      restaurantId: restaurantId,
      visitDate: visitTime,
      isConfirmed: isConfirmed,
    );
  }
}

/// VisitNotifier Provider
final visitNotifierProvider =
    StateNotifierProvider<VisitNotifier, AsyncValue<void>>((ref) {
      final repository = ref.watch(visitRepositoryProvider);
      return VisitNotifier(repository, ref);
    });

/// 특정 맛집의 마지막 방문일 Provider
final lastVisitDateProvider = FutureProvider.family<DateTime?, String>((
  ref,
  restaurantId,
) async {
  final repository = ref.watch(visitRepositoryProvider);
  return repository.getLastVisitDate(restaurantId);
});

/// 기간별 방문 기록 Provider
final visitRecordsByPeriodProvider =
    FutureProvider.family<
      List<VisitRecord>,
      ({DateTime startDate, DateTime endDate})
    >((ref, params) async {
      final allRecords = await ref.watch(visitRecordsProvider.future);
      return allRecords.where((record) {
        return record.visitDate.isAfter(params.startDate) &&
            record.visitDate.isBefore(
              params.endDate.add(const Duration(days: 1)),
            );
      }).toList()..sort((a, b) => b.visitDate.compareTo(a.visitDate));
    });

/// 주간 방문 통계 Provider (최근 7일)
final weeklyVisitStatsProvider = FutureProvider<Map<String, int>>((ref) async {
  final now = DateTime.now();
  final startOfWeek = DateTime(
    now.year,
    now.month,
    now.day,
  ).subtract(const Duration(days: 6));
  final records = await ref.watch(
    visitRecordsByPeriodProvider((startDate: startOfWeek, endDate: now)).future,
  );

  final stats = <String, int>{};
  for (var i = 0; i < 7; i++) {
    final date = startOfWeek.add(Duration(days: i));
    final dateKey = '${date.month}/${date.day}';
    stats[dateKey] = records
        .where(
          (r) =>
              r.visitDate.year == date.year &&
              r.visitDate.month == date.month &&
              r.visitDate.day == date.day,
        )
        .length;
  }
  return stats;
});

/// 자주 방문하는 맛집 Provider (상위 10개)
final frequentRestaurantsProvider =
    FutureProvider<List<({String restaurantId, int visitCount})>>((ref) async {
      final allRecords = await ref.watch(visitRecordsProvider.future);

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

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

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

/// 방문 기록 정렬 옵션
enum VisitSortOption {
  dateDesc, // 최신순
  dateAsc, // 오래된순
  restaurant, // 맛집별
}

/// 정렬된 방문 기록 Provider
final sortedVisitRecordsProvider =
    Provider.family<AsyncValue<List<VisitRecord>>, VisitSortOption>((
      ref,
      sortOption,
    ) {
      final recordsAsync = ref.watch(visitRecordsProvider);

      return recordsAsync.when(
        data: (records) {
          final sorted = List<VisitRecord>.from(records);
          switch (sortOption) {
            case VisitSortOption.dateDesc:
              sorted.sort((a, b) => b.visitDate.compareTo(a.visitDate));
              break;
            case VisitSortOption.dateAsc:
              sorted.sort((a, b) => a.visitDate.compareTo(b.visitDate));
              break;
            case VisitSortOption.restaurant:
              sorted.sort((a, b) => a.restaurantId.compareTo(b.restaurantId));
              break;
          }
          return AsyncValue.data(sorted);
        },
        loading: () => const AsyncValue.loading(),
        error: (error, stack) => AsyncValue.error(error, stack),
      );
    });

/// 카테고리별 방문 통계 Provider
final categoryVisitStatsProvider = FutureProvider<Map<String, int>>((
  ref,
) async {
  final allRecords = await ref.watch(visitRecordsProvider.future);
  final restaurantsAsync = await ref.watch(restaurantListProvider.future);

  final categoryCount = <String, int>{};

  for (final record in allRecords) {
    final restaurant = restaurantsAsync
        .where((r) => r.id == record.restaurantId)
        .firstOrNull;
    if (restaurant != null) {
      categoryCount[restaurant.category] =
          (categoryCount[restaurant.category] ?? 0) + 1;
    }
  }

  return categoryCount;
});
