import 'dart:async';

import 'package:be_happy/core/result.dart';
import 'package:be_happy/domain/entities/client_notification.dart';
import 'package:be_happy/domain/entities/map_settings.dart';
import 'package:be_happy/domain/usecase/check_user_usecase.dart';
import 'package:be_happy/domain/usecase/get_available_zones_usecase.dart';
import 'package:be_happy/domain/usecase/get_client_orders_usecase.dart';
import 'package:be_happy/domain/usecase/get_map_settings_usecase.dart';
import 'package:be_happy/domain/usecase/get_notifications_stream_usecase.dart';
import 'package:be_happy/domain/usecase/logout_usecase.dart';
import 'package:be_happy/presentation/viewmodel/splash_bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import '../../domain/entities/point.dart';
import '../../domain/entities/scooter.dart';
import '../../domain/entities/scooter_order.dart';
import '../../domain/entities/zone.dart';
import '../../domain/usecase/get_available_scooters_usecase.dart';
import '../../domain/usecase/get_profile_usecase.dart';
import '../event/map_event.dart';
import '../event/spalsh_event.dart';
import '../state/map_state.dart';
import 'package:maps_toolkit/maps_toolkit.dart' as mt;

class MapBloc extends Bloc<ScooterEvent, ScooterState> {
  final GetAvailableScootersUsecase getScootersUsecase;
  final GetClientOrdersUsecase getClientOrdersUsecase;
  final GetAvailableZonesUsecase getAvailableZonesUsecase;
  final GetMapSettingsUsecase getMapSettingsUsecase;
  final GetNotificationsStreamUseCase getNotificationsStreamUseCase;
  final GetProfileUseCase getProfileUseCase;
  final CheckUserUseCase checkUserUseCase;
  final LogoutUseCase logoutUseCase;
  final SplashBloc splashBloc;
  StreamSubscription<ClientNotification>? _notificationSubscription;

  MapBloc(
    this.getAvailableZonesUsecase,
    this.getScootersUsecase,
    this.getClientOrdersUsecase,
    this.getMapSettingsUsecase,
    this.getNotificationsStreamUseCase,
    this.getProfileUseCase,
    this.checkUserUseCase,
    this.logoutUseCase,
    this.splashBloc,
  ) : super(ScooterState(isGeomarksShowed: true)) {
    on<FetchScooters>(_onFetchScooters);
    on<UpdateMap>(_onUpdateMap);
    on<UpdateUserLocation>(_onUpdateUserLocation);
    on<NotificationReceived>(_onNotificationReceived);
    on<FetchProfileData>(_onFetchProfileData);
    on<CheckUser>(_onCheckUser);
    on<LogoutPressed>(_onLogoutPressed);
    on<FocusOnScooter>(_onFocusOnScooter);
    on<ClearMapPlacemarks>(_onClearMapPlacemarks);
    on<ClearMapFocus>(_onClearMapFocus);
  }

  void startNotificationStream() {
    _notificationSubscription?.cancel();

    _notificationSubscription = getNotificationsStreamUseCase().listen(
      (notification) {
        add(NotificationReceived(notification));
      },
      onError: (error) {
        print(" SSE BLOC ERROR: $error");
        _handleReconnect();
      },
      onDone: () {
        print(" SSE Stream closed by server (onDone)");
        _handleReconnect();
      },
      cancelOnError: true,
    );
  }

  void _handleReconnect() {
    if (isClosed) return;

    Future.delayed(const Duration(seconds: 5), () {
      if (!isClosed) {
        print(" Attempting to reconnect to SSE...");
        startNotificationStream();
      }
    });
  }

  void stopNotificationStream() {
    _notificationSubscription?.cancel();
    _notificationSubscription = null;
  }

  Future<void> _onFetchScooters(
    FetchScooters event,
    Emitter<ScooterState> emit,
  ) async {
    emit(state.copyWith(status: ScooterStatus.loading));

    try {
      final results = await Future.wait([
        getScootersUsecase(event.areaScooters, 0, 100),
        getAvailableZonesUsecase(event.area, 0, 100),
        getMapSettingsUsecase(),
        getClientOrdersUsecase(),
      ]);

      final scooters = results[0] as List<Scooter>;
      final zones = results[1] as List<Zone>;
      final settings = results[2] as MapSettings;
      final orders = results[3];



      zones.forEach(print);

      List<Zone> filteredZones = [];

      if (settings.all_zones) {
        if (settings.parking_zones) {
          filteredZones.addAll(zones.where((el) => el.type == "Finish"));
        }
        if (settings.restricted_parking_zones) {
          filteredZones.addAll(zones.where((el) => el.type == "Drive"));
        }
        if (settings.restricted_driving_zones) {
          filteredZones.addAll(zones.where((el) => el.type == "NotDrive"));
        }
      }

      print(
        " FETCH: filteredZones.length = ${filteredZones.length}, all_zones=${settings.all_zones}",
      );

      emit(
        state.copyWith(
          status: ScooterStatus.success,
          scooters: scooters,
          // reservedScooters: reservedScooters,
          zones: filteredZones,
          area: event.area,
          areaScooters: event.areaScooters,
          isGeomarksShowed: settings.all_placemarks,
        ),
      );
    } catch (e) {
      print("FETCH ERROR: $e");
      emit(
        state.copyWith(
          status: ScooterStatus.failure,
          errorMessage: e.toString(),
        ),
      );
    }
  }

  Future<void> _onUpdateMap(UpdateMap event, Emitter<ScooterState> emit) async {
    emit(state.copyWith(status: ScooterStatus.loading));

    try {
      final results = await Future.wait([
        getScootersUsecase(state.areaScooters, 0, 100),
        getAvailableZonesUsecase(state.area, 0, 100),
        getMapSettingsUsecase(),
      ]);

      final scooters = results[0] as List<Scooter>;
      final zones = results[1] as List<Zone>;
      final settings = results[2] as MapSettings;

      List<Zone> filteredZones = [];

      if (settings.all_zones) {
        if (settings.parking_zones) {
          filteredZones.addAll(zones.where((el) => el.type == "Finish"));
        }
        if (settings.restricted_parking_zones) {
          filteredZones.addAll(zones.where((el) => el.type == "Drive"));
        }
        if (settings.restricted_driving_zones) {
          filteredZones.addAll(zones.where((el) => el.type == "NotDrive"));
        }
      }

      print(
        " UPDATE MAP: filteredZones.length = ${filteredZones.length}, all_zones=${settings.all_zones}",
      );

      final zonesToEmit = filteredZones.isNotEmpty
          ? filteredZones
          : state.zones;

      emit(
        state.copyWith(
          status: ScooterStatus.success,
          scooters: scooters,
          zones: zonesToEmit,
          isGeomarksShowed: settings.all_placemarks,
        ),
      );
    } catch (e) {
      print("UPDATE ERROR: $e");
      emit(
        state.copyWith(
          status: ScooterStatus.failure,
          errorMessage: e.toString(),
        ),
      );
    }
  }

  FutureOr<void> _onUpdateUserLocation(
    UpdateUserLocation event,
    Emitter<ScooterState> emit,
  ) {
    print("USER LOCATION UPDATED EVENT start");
    state.zones.forEach((z) {
      if (checkUserInZone(Point(event.latitude, event.longitude), z.points)) {
        print("USER IN ZONE - ${z.type}");
      }
    });
  }

  FutureOr<void> _onNotificationReceived(
    NotificationReceived event,
    Emitter<ScooterState> emit,
  ) {
    print("NOTIFICATION RECEIVED: ${event.notification.content}");
    emit(state.copyWith(lastNotification: event.notification));
  }

  bool checkUserInZone(Point userPos, List<Point> zonePoints) {
    List<mt.LatLng> polygon = zonePoints
        .map((p) => mt.LatLng(p.latitude, p.longitude))
        .toList();

    mt.LatLng userLatLng = mt.LatLng(userPos.latitude, userPos.longitude);

    return mt.PolygonUtil.containsLocation(userLatLng, polygon, true);
  }

  FutureOr<void> _onFetchProfileData(
    FetchProfileData event,
    Emitter<ScooterState> emit,
  ) async {
    try {
      final profile = await getProfileUseCase();

      emit(
        state.copyWith(phoneNumber: profile.phone, balance: profile.balance),
      );
    } catch (e) {
      emit(
        state.copyWith(
          status: ScooterStatus.failure,
          errorMessage: "FetchProfileData for SideMenu: ${e.toString()}",
        ),
      );
    }
  }

  FutureOr<void> _onLogoutPressed(
    LogoutPressed event,
    Emitter<ScooterState> emit,
  ) {
    logoutUseCase();
    splashBloc.add(AuthCheckRequested());
  }

  FutureOr<void> _onCheckUser(
    CheckUser event,
    Emitter<ScooterState> emit,
  ) async {
    try {
      final flags = await checkUserUseCase();

      print("flags: $flags");

      if (flags == null) {
        return;
      }

      print("check user success");

      emit(state.copyWith(flags: flags));
    } catch (e) {
      emit(
        state.copyWith(
          status: ScooterStatus.failure,
          errorMessage: "CheckUser: ${e.toString()}",
        ),
      );
    }
  }

  FutureOr<void> _onFocusOnScooter(FocusOnScooter event, Emitter<ScooterState> emit) {
    final updatedReserved = List<Scooter>.from(state.reservedScooters ?? []);
    if (!updatedReserved.any((s) => s.id == event.scooter.id)) {
      updatedReserved.add(event.scooter);
    }

    emit(state.copyWith(
      selectedScooterForFocus: event.scooter,
      reservedScooters: updatedReserved,
    ));
  }



  FutureOr<void> _onClearMapPlacemarks(ClearMapPlacemarks event, Emitter<ScooterState> emit) async {
    try{
      final orders = await getClientOrdersUsecase();

      List<Scooter> updatedReservedScooters = [];

      if (orders is Success<List<ScooterOrder>>) {
        print("FETCH: orders.data.length = ${orders.data?.length}");

        updatedReservedScooters = orders.data?.map((order) {
          return order.scooter;
        }).toList() ?? [];
      }

      emit(state.copyWith(
        reservedScooters: updatedReservedScooters,
      ));
    } catch (e) {
      print("Error in _onClearMapPlacemarks: $e");
      emit(state.copyWith(
        status: ScooterStatus.failure,
        errorMessage: e.toString(),
      ));
    }
  }

  FutureOr<void> _onClearMapFocus(ClearMapFocus event, Emitter<ScooterState> emit) {
    emit(state.copyWith(
      selectedScooterForFocus: null,
    ));
  }
}


