import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:geolocator/geolocator.dart';
import 'package:lunchpick/core/utils/app_logger.dart';
import 'package:permission_handler/permission_handler.dart';

const double _defaultLatitude = 37.5666805;
const double _defaultLongitude = 126.9784147;

/// 위치 정보를 사용할 수 없을 때 활용하는 기본 좌표(서울 시청).
Position defaultPosition() {
  return Position(
    latitude: _defaultLatitude,
    longitude: _defaultLongitude,
    timestamp: DateTime.now(),
    accuracy: 0,
    altitude: 0,
    altitudeAccuracy: 0,
    heading: 0,
    headingAccuracy: 0,
    speed: 0,
    speedAccuracy: 0,
    isMocked: false,
  );
}

/// 위치 권한 상태 Provider
final locationPermissionProvider = FutureProvider<PermissionStatus>((
  ref,
) async {
  return await Permission.location.status;
});

/// 현재 위치 Provider
final currentLocationProvider = FutureProvider<Position?>((ref) async {
  // 위치 권한 확인
  final permissionStatus = await Permission.location.status;

  if (!permissionStatus.isGranted) {
    // 권한이 없으면 요청
    final result = await Permission.location.request();
    if (!result.isGranted) {
      AppLogger.debug('위치 권한 거부됨, 기본 좌표(서울 시청) 사용');
      return defaultPosition();
    }
  }

  // 위치 서비스 활성화 확인
  final serviceEnabled = await Geolocator.isLocationServiceEnabled();
  if (!serviceEnabled) {
    AppLogger.debug('위치 서비스 비활성화, 기본 좌표(서울 시청) 사용');
    return defaultPosition();
  }

  // 현재 위치 가져오기
  try {
    return await Geolocator.getCurrentPosition(
      desiredAccuracy: LocationAccuracy.high,
      timeLimit: const Duration(seconds: 10),
    );
  } catch (e) {
    // 타임아웃이나 오류 발생 시 마지막 알려진 위치 반환
    final lastPosition = await Geolocator.getLastKnownPosition();
    if (lastPosition != null) {
      return lastPosition;
    }
    AppLogger.debug('현재 위치를 가져오지 못해 기본 좌표(서울 시청)를 반환');
    return defaultPosition();
  }
});

/// 위치 스트림 Provider
final locationStreamProvider = StreamProvider<Position>((ref) async* {
  if (kIsWeb) {
    AppLogger.debug('[location] web detected, emit fallback immediately');
    yield defaultPosition();
    return;
  }

  final status = await Permission.location.status;
  if (!status.isGranted) {
    AppLogger.debug('[location] permission not granted, emit fallback');
    yield defaultPosition();
    return;
  }

  try {
    yield* Geolocator.getPositionStream(
      locationSettings: const LocationSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 10, // 10미터 이상 이동 시 업데이트
      ),
    );
  } catch (_) {
    AppLogger.error('[location] position stream failed, emit fallback');
    yield defaultPosition();
  }
});

/// 초기 3초 내 위치를 가져오지 못하면 기본 좌표를 우선 반환하고,
/// 이후 실제 위치 스트림이 들어오면 업데이트하는 Provider.
final currentLocationWithFallbackProvider = StreamProvider<Position>((
  ref,
) async* {
  AppLogger.debug('[location] emit fallback immediately (safe start)');
  // 웹/권한 거부 상황에서는 즉시 기본 좌표를 먼저 흘려보내 리스트 로딩을 막는다.
  final fallback = defaultPosition();
  yield fallback;

  final initial = await Future.any([
    ref.watch(currentLocationProvider.future).then((pos) => pos ?? fallback),
    Future<Position>.delayed(const Duration(seconds: 3), () => fallback),
  ]).catchError((_) => fallback);

  if (initial.latitude != fallback.latitude ||
      initial.longitude != fallback.longitude) {
    AppLogger.debug(
      '[location] resolved initial position: '
      '${initial.latitude}, ${initial.longitude}',
    );
    yield initial;
  } else {
    AppLogger.debug('[location] initial resolved to fallback');
  }

  yield* ref.watch(locationStreamProvider.stream).handleError((_) {
    // 스트림 오류는 무시하고 마지막 위치를 유지
    AppLogger.error('[location] stream error, keeping last position');
  });
});

/// 위치 관리 StateNotifier
class LocationNotifier extends StateNotifier<AsyncValue<Position?>> {
  LocationNotifier() : super(const AsyncValue.loading());

  /// 위치 권한 요청
  Future<bool> requestLocationPermission() async {
    try {
      final status = await Permission.location.request();
      return status.isGranted;
    } catch (e) {
      return false;
    }
  }

  /// 위치 서비스 활성화 요청
  Future<bool> requestLocationService() async {
    try {
      return await Geolocator.openLocationSettings();
    } catch (e) {
      return false;
    }
  }

  /// 현재 위치 가져오기
  Future<void> getCurrentLocation() async {
    state = const AsyncValue.loading();

    try {
      // 권한 확인
      final permissionStatus = await Permission.location.status;
      if (!permissionStatus.isGranted) {
        final granted = await requestLocationPermission();
        if (!granted) {
          AppLogger.debug('위치 권한 거부됨, 기본 좌표(서울 시청)로 대체');
          state = AsyncValue.data(defaultPosition());
          return;
        }
      }

      // 위치 서비스 확인
      final serviceEnabled = await Geolocator.isLocationServiceEnabled();
      if (!serviceEnabled) {
        AppLogger.debug('위치 서비스 비활성화, 기본 좌표(서울 시청)로 대체');
        state = AsyncValue.data(defaultPosition());
        return;
      }

      // 위치 가져오기
      final position = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.high,
        timeLimit: const Duration(seconds: 10),
      );

      state = AsyncValue.data(position);
    } catch (e) {
      // 오류 발생 시 마지막 알려진 위치 시도
      try {
        final lastPosition = await Geolocator.getLastKnownPosition();
        if (lastPosition != null) {
          state = AsyncValue.data(lastPosition);
        } else {
          AppLogger.debug('마지막 위치도 없어 기본 좌표(서울 시청)로 대체');
          state = AsyncValue.data(defaultPosition());
        }
      } catch (_) {
        state = AsyncValue.data(defaultPosition());
      }
    }
  }

  /// 두 지점 간의 거리 계산 (미터 단위)
  double calculateDistance(
    double startLatitude,
    double startLongitude,
    double endLatitude,
    double endLongitude,
  ) {
    return Geolocator.distanceBetween(
      startLatitude,
      startLongitude,
      endLatitude,
      endLongitude,
    );
  }
}

/// LocationNotifier Provider
final locationNotifierProvider =
    StateNotifierProvider<LocationNotifier, AsyncValue<Position?>>((ref) {
      return LocationNotifier();
    });
