import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:geolocator/geolocator.dart';
import '../../domain/usecases/get_todays_shift_usecase.dart';
import '../../domain/usecases/get_attendance_status_usecase.dart';
import '../../domain/usecases/clock_in_usecase.dart';
import '../../domain/usecases/clock_out_usecase.dart';
import '../../domain/usecases/get_activity_log_usecase.dart';
import '../../domain/arguments/clock_in_arguments.dart';
import '../../domain/arguments/clock_out_arguments.dart';
import 'clock_in_event.dart';
import 'clock_in_state.dart';

class ClockInBloc extends Bloc<ClockInEvent, ClockInState> {
  final GetTodaysShiftUseCase _getTodaysShift;
  final GetAttendanceStatusUseCase _getAttendanceStatus;
  final ClockInUseCase _clockIn;
  final ClockOutUseCase _clockOut;
  final GetActivityLogUseCase _getActivityLog;

  // Mock Venue Location (e.g., Grand Hotel, NYC)
  static const double venueLat = 40.7128;
  static const double venueLng = -74.0060;
  static const double allowedRadiusMeters = 500;

  ClockInBloc({
    required GetTodaysShiftUseCase getTodaysShift,
    required GetAttendanceStatusUseCase getAttendanceStatus,
    required ClockInUseCase clockIn,
    required ClockOutUseCase clockOut,
    required GetActivityLogUseCase getActivityLog,
  })  : _getTodaysShift = getTodaysShift,
        _getAttendanceStatus = getAttendanceStatus,
        _clockIn = clockIn,
        _clockOut = clockOut,
        _getActivityLog = getActivityLog,
        super(ClockInState(selectedDate: DateTime.now())) {
    on<ClockInPageLoaded>(_onLoaded);
    on<DateSelected>(_onDateSelected);
    on<CheckInRequested>(_onCheckIn);
    on<CheckOutRequested>(_onCheckOut);
    on<CheckInModeChanged>(_onModeChanged);
    on<RequestLocationPermission>(_onRequestLocationPermission);
    on<CommuteModeToggled>(_onCommuteModeToggled);
    on<LocationUpdated>(_onLocationUpdated);

    add(ClockInPageLoaded());
  }

  Future<void> _onLoaded(
    ClockInPageLoaded event,
    Emitter<ClockInState> emit,
  ) async {
    emit(state.copyWith(status: ClockInStatus.loading));
    try {
      final shift = await _getTodaysShift();
      final status = await _getAttendanceStatus();
      final activity = await _getActivityLog();

      // Check permissions silently on load? Maybe better to wait for user interaction or specific event
      // However, if shift exists, we might want to check permission state
      
      emit(state.copyWith(
        status: ClockInStatus.success,
        todayShift: shift,
        attendance: status,
        activityLog: activity,
      ));

      if (shift != null && !status.isCheckedIn) {
        add(RequestLocationPermission());
      }

    } catch (e) {
      emit(state.copyWith(
        status: ClockInStatus.failure,
        errorMessage: e.toString(),
      ));
    }
  }

  Future<void> _onRequestLocationPermission(
    RequestLocationPermission event,
    Emitter<ClockInState> emit,
  ) async {
    try {
      LocationPermission permission = await Geolocator.checkPermission();
      if (permission == LocationPermission.denied) {
        permission = await Geolocator.requestPermission();
      }
      
      final hasConsent = permission == LocationPermission.always || permission == LocationPermission.whileInUse;
      
      emit(state.copyWith(hasLocationConsent: hasConsent));

      if (hasConsent) {
        _startLocationUpdates();
      }
    } catch (e) {
      emit(state.copyWith(errorMessage: "Location error: $e"));
    }
  }

  void _startLocationUpdates() async {
    try {
      final position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
      final distance = Geolocator.distanceBetween(
        position.latitude,
        position.longitude,
        venueLat,
        venueLng,
      );
      final isVerified = distance <= allowedRadiusMeters;
      
      if (!isClosed) {
        add(LocationUpdated(position: position, distance: distance, isVerified: isVerified));
      }
    } catch (e) {
       // Handle error silently or via state
    }
  }

  void _onLocationUpdated(
    LocationUpdated event,
    Emitter<ClockInState> emit,
  ) {
    emit(state.copyWith(
      currentLocation: event.position,
      distanceFromVenue: event.distance,
      isLocationVerified: event.isVerified,
      etaMinutes: (event.distance / 80).round(), // Rough estimate: 80m/min walking speed
    ));
  }

  void _onCommuteModeToggled(
    CommuteModeToggled event,
    Emitter<ClockInState> emit,
  ) {
     emit(state.copyWith(isCommuteModeOn: event.isEnabled));
     if (event.isEnabled) {
       add(RequestLocationPermission());
     }
  }

  void _onDateSelected(
    DateSelected event,
    Emitter<ClockInState> emit,
  ) {
    emit(state.copyWith(selectedDate: event.date));
  }

  void _onModeChanged(
    CheckInModeChanged event,
    Emitter<ClockInState> emit,
  ) {
    emit(state.copyWith(checkInMode: event.mode));
  }

  Future<void> _onCheckIn(
    CheckInRequested event,
    Emitter<ClockInState> emit,
  ) async {
    // Only verify location if not using NFC (or depending on requirements) - enforcing for swipe
    if (state.checkInMode == 'swipe' && !state.isLocationVerified) {
        // Allow for now since coordinates are hardcoded and might not match user location
        // emit(state.copyWith(errorMessage: "You must be at the location to clock in."));
        // return;
    }

    emit(state.copyWith(status: ClockInStatus.actionInProgress));
    try {
      final newStatus = await _clockIn(
        ClockInArguments(shiftId: event.shiftId, notes: event.notes),
      );
      emit(state.copyWith(
        status: ClockInStatus.success,
        attendance: newStatus,
      ));
    } catch (e) {
      emit(state.copyWith(
        status: ClockInStatus.failure,
        errorMessage: e.toString(),
      ));
    }
  }

  Future<void> _onCheckOut(
    CheckOutRequested event,
    Emitter<ClockInState> emit,
  ) async {
    emit(state.copyWith(status: ClockInStatus.actionInProgress));
    try {
      final newStatus = await _clockOut(
        ClockOutArguments(
          notes: event.notes,
          breakTimeMinutes: 0, // Should be passed from event if supported
        ),
      );
      emit(state.copyWith(
        status: ClockInStatus.success,
        attendance: newStatus,
      ));
    } catch (e) {
      emit(state.copyWith(
        status: ClockInStatus.failure,
        errorMessage: e.toString(),
      ));
    }
  }
}
