import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:bot_toast/bot_toast.dart';
import 'package:be_happy/domain/entities/client_notification.dart';
import 'package:be_happy/domain/usecase/book_scooter_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_payment_cards_usecase.dart';
import 'package:be_happy/domain/usecase/get_pedestrian_routes_usecase.dart';
import 'package:be_happy/domain/usecase/get_scooter_usecase.dart';
import 'package:be_happy/domain/usecase/save_map_settings_usecase.dart';
import 'package:be_happy/presentation/components/fine_notification_card.dart';
import 'package:be_happy/presentation/components/map_icon_painter/clusterized_icon_painter.dart';
import 'package:be_happy/presentation/components/payment_notification_card.dart';
import 'package:be_happy/presentation/components/sheet/current_rides_sheet.dart';
import 'package:be_happy/presentation/components/sheet/map_settings_sheet.dart';
import 'package:be_happy/presentation/components/sheet/tariff_sheet.dart';
import 'package:be_happy/presentation/event/current_rides_event.dart';
import 'package:be_happy/presentation/event/map_settings_modal_event.dart';
import 'package:be_happy/presentation/viewmodel/current_rides_bloc.dart';
import 'package:be_happy/presentation/viewmodel/map_settings_modal_bloc.dart';
import 'package:be_happy/presentation/viewmodel/tariff_sheet_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:geolocator/geolocator.dart';
import 'package:go_router/go_router.dart';
import 'package:yandex_mapkit/yandex_mapkit.dart';

import '../../core/app_colors.dart';
import '../../di/service_locator.dart';
import '../../domain/entities/scooter.dart';
import '../../domain/entities/zone.dart';
import '../../domain/usecase/get_address_by_point_usecase.dart';
import '../../domain/usecase/get_available_tariffs_usecase.dart';
import '../../domain/usecase/get_notifications_stream_usecase.dart';
import '../components/notification_toast.dart';
import '../components/sheet/scooter_bottom_sheet.dart';
import '../components/side_menu.dart';
import '../components/unpaid_order_notification_card.dart';
import '../event/map_event.dart';
import '../event/scooter_detail_modal_event.dart';
import '../state/map_state.dart';
import '../viewmodel/map_bloc.dart';
import '../viewmodel/scooter_detail_modal_bloc.dart';

class MapScreen extends StatefulWidget {
  const MapScreen({super.key});

  @override
  State<MapScreen> createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  YandexMapController? mapController;
  Position? _currentPosition;
  StreamSubscription<Position>? _positionStreamSubscription;
  StreamSubscription<ClientNotification>? _notificationStreamSubscription;
  bool _isFirstLocationUpdate = true;
  Timer? _debounceTimer;

  @override
  void initState() {
    super.initState();
    _checkLocationPermission();
    _initScooterIcon();
    _startNotificationStream();
    context.read<MapBloc>().add(FetchProfileData());
    context.read<MapBloc>().add(CheckUser());
  }

  @override
  void dispose() {
    _debounceTimer?.cancel();
    _positionStreamSubscription?.cancel();
    _notificationStreamSubscription?.cancel();
    context.read<MapBloc>().stopNotificationStream();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.transparent,
      drawer: const SideMenu(),
      body: Container(
        decoration: const BoxDecoration(gradient: AppColors.phoneScreenBg),
        child: SafeArea(
          child: Stack(
            children: [
              BlocConsumer<MapBloc, ScooterState>(
                listenWhen: (previous, current) {
                  return current.lastNotification !=
                      previous.lastNotification ||
                      current.flags != previous.flags ||
                      previous.selectedScooterForFocus?.id
                          != current.selectedScooterForFocus?.id;
                },

                listener: (context, state) {
                  if (state.lastNotification != null) {
                    _showNotificationToast(state.lastNotification!);
                  }

                  if (state.flags != null) {
                    if (!state.flags.hasCard) {
                      BotToast.showCustomNotification(
                        duration: null,
                        toastBuilder: (_) {
                          return Container(
                            margin: const EdgeInsets.only(top: 120),
                            child: Material(
                              color: Colors.transparent,
                              child: PaymentNotificationCard(
                                onBindCard: () {
                                  BotToast.cleanAll();
                                  context.push("/home/payment-methods");
                                },
                                onClose: () => BotToast.cleanAll(),
                              ),
                            ),
                          );
                        },
                      );
                    }
                    if (state.flags.hasFine) {
                      BotToast.showCustomNotification(
                        duration: null,
                        toastBuilder: (_) {
                          return Container(
                            margin: const EdgeInsets.only(top: 120),
                            child: Material(
                              color: Colors.transparent,
                              child: FineNotificationCard(
                                /*onBindCard: () {
                                  BotToast.cleanAll();
                                  context.push("/home/payment-methods");
                                },*/
                                onClose: () => BotToast.cleanAll(),
                              ),
                            ),
                          );
                        },
                      );
                    }

                    if (state.flags.hasUnpaidOrder) {
                      BotToast.showCustomNotification(
                        duration: null,
                        toastBuilder: (_) {
                          return Container(
                            margin: const EdgeInsets.only(top: 120),
                            child: Material(
                              color: Colors.transparent,
                              child: UnpaidOrderNotificationCard(
                                /*onBindCard: () {
                                  BotToast.cleanAll();
                                  context.push("/home/payment-methods");
                                },*/
                                onClose: () => BotToast.cleanAll(),
                              ),
                            ),
                          );
                        },
                      );
                    }
                  }

                  if (state.selectedScooterForFocus != null) {
                    final targetScooter = state.selectedScooterForFocus!;

                    print("RESERVED SCOOTER: $targetScooter");

                    _moveCameraToPoint(
                      targetScooter.longitude,
                      targetScooter.latitude,
                      zoom: 17,
                    );

                    context.read<MapBloc>().add(ClearMapFocus());
                  }
                },
                buildWhen: (previous, current) {
                  return previous.scooters != current.scooters ||
                      previous.reservedScooters != current.reservedScooters ||
                      previous.zones != current.zones ||
                      previous.status != current.status;
                },

                builder: (context, state) {
                  final freeScooters = _buildScooterPlacemarks(
                    scooters: state.scooters,
                    iconAsset: 'assets/icons/scooter_placemark_fill.png',
                    isClickable: true,
                  );

                  final reservedScooters = _buildScooterPlacemarks(
                    scooters: state.reservedScooters ?? [],
                    iconAsset: 'assets/icons/scooter_reserved_placemark_fill.png',
                    isClickable: false,
                  );

                  final zonePolygons = _buildZonePolygons(state.zones);

                  return RepaintBoundary(
                    child: YandexMap(
                      onMapCreated: (controller) {
                        controller.toggleUserLayer(visible: true);
                        mapController = controller;
                        if (_currentPosition != null) {
                          _fetchScooters();
                        }
                      },
                      onCameraPositionChanged:
                          (cameraPosition, reason, finished) {
                        if (finished) {
                          _fetchScooters();
                        }
                      },
                      mapObjects: [
                        ...zonePolygons,
                        ...reservedScooters,
                        ClusterizedPlacemarkCollection(
                          mapId: const MapObjectId('scooters_cluster'),
                          placemarks: freeScooters,
                          radius: 30,
                          minZoom: 15,
                          consumeTapEvents: true,
                          onClusterTap: (collection, cluster) {
                            final clusteredPlacemarks = cluster.placemarks;
                            final filtered = state.scooters.where((scooter) {
                              return clusteredPlacemarks.any(
                                    (pm) => pm.mapId.value == scooter.id.toString(),
                              );
                            }).toList();
                            _onMarkerTap(filtered);
                          },
                          onClusterAdded: (self, cluster) async {
                            return cluster.copyWith(
                              appearance: cluster.appearance.copyWith(
                                opacity: 1.0,
                                icon: PlacemarkIcon.single(
                                  PlacemarkIconStyle(
                                    image: BitmapDescriptor.fromBytes(
                                      await ClusterIconPainter(
                                        cluster.size,
                                      ).getClusterIconBytes(),
                                    ),
                                    scale: 0.8,
                                  ),
                                ),
                              ),
                            );
                          },
                        ),
                      ],
                    ),
                  );
                },
              ),

              BlocBuilder<MapBloc, ScooterState>(
                buildWhen: (previous, current) =>
                previous.status != current.status,
                builder: (context, state) {
                  if (state.status == ScooterStatus.loading) {
                    return const Positioned(
                      top: 80,
                      left: 0,
                      right: 0,
                      child: Center(child: CircularProgressIndicator()),
                    );
                  }
                  return const SizedBox.shrink();
                },
              ),

              // Кнопки управления (Меню, Уведомления)
              _buildTopButtons(),

              // Кнопки навигации
              if (_currentPosition != null) _buildSideControls(),

              _buildCentralQrButton(),
            ],
          ),
        ),
      ),
    );
  }

  void _startNotificationStream() {
    final notificationsStreamUseCase = getIt<GetNotificationsStreamUseCase>();

    _notificationStreamSubscription = notificationsStreamUseCase().listen(
      (notification) {
        if (mounted) {
          context.read<MapBloc>().add(NotificationReceived(notification));
        }
      },
      onError: (error) {
        print("SSE NOTIFICATION ERROR: $error");
      },
    );
  }

  void _checkLocationPermission() async {
    final permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied ||
        permission == LocationPermission.deniedForever) {
      await Geolocator.requestPermission();
    }
    _getCurrentLocation();
    // _startTrackingLocation();
  }

  void _startTrackingLocation() {
    const LocationSettings locationSettings = LocationSettings(
      accuracy: LocationAccuracy.high,
      distanceFilter: 10,
    );

    _positionStreamSubscription =
        Geolocator.getPositionStream(
          locationSettings: locationSettings,
        ).listen((Position position) {
          if (!mounted) return;

          print(
            "-----------------------------------------------------      tracking...     --------------------------------------------------------",
          );

          setState(() => _currentPosition = position);

          context.read<MapBloc>().add(
            UpdateUserLocation(position.latitude, position.longitude),
          );

          if (_isFirstLocationUpdate) {
            _moveCameraToPoint(position.latitude, position.longitude, zoom: 15);
            _isFirstLocationUpdate = false;
          }
          _fetchScooters();
        });
  }

  void _getCurrentLocation() async {
    try {
      final position = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.high,
      );
      setState(() => _currentPosition = position);

      if (mapController != null && mounted) {
        await _moveCameraToPoint(position.latitude, position.longitude);
        _fetchScooters();
      }
    } catch (e) {
      debugPrint('Ошибка геолокации: $e');
    }
  }

  void _fetchScooters() async {
    final controller = mapController;
    if (controller == null) return;

    if (_debounceTimer?.isActive ?? false) _debounceTimer!.cancel();

    _debounceTimer = Timer(const Duration(milliseconds: 500), () async {
      final visibleRegion = await controller.getVisibleRegion();

      final areaScooters = [
        visibleRegion.bottomRight.longitude,
        visibleRegion.bottomRight.latitude,
        visibleRegion.topLeft.latitude,
        visibleRegion.topLeft.longitude,
      ];

      final areaZones = [
        visibleRegion.bottomRight.longitude,
        visibleRegion.bottomRight.latitude,
        visibleRegion.topLeft.longitude,
        visibleRegion.topLeft.latitude,
      ];

      if (mounted) {
        context.read<MapBloc>().add(FetchScooters(areaZones, areaScooters));
      }
    });
  }
  Future<void> _moveCameraToPoint(
    double lat,
    double lon, {
    double zoom = 15,
  }) async {
    await mapController?.moveCamera(
      CameraUpdate.newCameraPosition(
        CameraPosition(
          target: Point(latitude: lat, longitude: lon),
          zoom: zoom,
        ),
      ),
    );
  }

  void _onMarkerTap(List<Scooter> scooters) async {
    context.read<MapBloc>().add(CheckUser());
    final flags = context.read<MapBloc>().state.flags;

    if (!flags.hasCard) {
      _showExistingNotification(
        child: PaymentNotificationCard(
          onBindCard: () {
            BotToast.cleanAll();
            context.push("/home/payment-methods");
          },
          onClose: () => BotToast.cleanAll(),
        ),
      );
      return;
    }
    if (flags.hasUnpaidOrder) {
      _showExistingNotification(
        child: UnpaidOrderNotificationCard(
          onClose: () => BotToast.cleanAll(),
        ),
      );
      return;
    }
    if (flags.hasFine) {
      _showExistingNotification(
        child: FineNotificationCard(
          onClose: () => BotToast.cleanAll(),
        ),
      );
      return;
    }

    context.push(
      "/home/scooter-sheet",
      extra: {'scooters': scooters, 'currentLocation': _currentPosition},
    );
  }

  void _onMapSettingsTap() {
    context.push("/home/map-settings-sheet");
    /*showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      backgroundColor: Colors.transparent,
      isDismissible: true,
      builder: (context) {
        return BlocProvider(
          create: (context) => MapSettingsModalBloc(
            getIt<GetMapSettingsUsecase>(),
            getIt<SaveMapSettingsUsecase>(),
          )..add(MapSettingsModalStarted()),
          child: MapSettingsSheet(),
        );
      },
    );*/
  }

  void _onNotificationTap() {
    context.push("/home/current-rides-sheet");

    /*showModalBottomSheet(
      context: context,
      builder: (context) => BlocProvider(
        create: (context) =>
            CurrentRidesBloc(getIt<GetClientOrdersUsecase>())
              ..add(LoadClientOrders()),
        child: CurrentRidesSheet(),
      ),
    );*/

    // BotToast.showCustomNotification(
    //   duration: const Duration(seconds: 4),
    //
    //   toastBuilder: (_) {
    //     return NotificationToast(
    //       title: "",
    //       onClose: () {
    //         BotToast.cleanAll();
    //       },
    //     );
    //   },
    // );
  }

  void _showNotificationToast(ClientNotification notification) {
    String title = _getNotificationTitle(notification.type);
    Color backgroundColor = _getNotificationColor(notification.type);

    BotToast.showCustomNotification(
      duration: null,
      toastBuilder: (_) {
        return Container(
          margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 50),
          child: Material(
            elevation: 8,
            shadowColor: Colors.black26,
            color: backgroundColor,
            borderRadius: BorderRadius.circular(12),
            clipBehavior: Clip.antiAlias,
            child: IntrinsicHeight(
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  Container(
                    width: 60,
                    child: Image.asset(
                      'assets/icons/clichnik.png',
                      fit: BoxFit.contain,
                    ),
                  ),
                  Expanded(
                    child: Padding(
                      padding: const EdgeInsets.all(12.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            title,
                            style: const TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 16,
                            ),
                          ),
                          const SizedBox(height: 4),
                          Text(
                            notification.content,
                            style: TextStyle(fontSize: 14),
                          ),
                        ],
                      ),
                    ),
                  ),
                  Align(
                    alignment: Alignment.topRight,
                    child: IconButton(
                      icon: const Icon(Icons.close, size: 20),
                      onPressed: () {
                        BotToast.cleanAll();
                      },
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      },
    );
  }

  String _getNotificationTitle(NotificationType type) {
    switch (type) {
      case NotificationType.info:
        return 'Информация';
      case NotificationType.attention:
        return 'Внимание';
      case NotificationType.warning:
        return 'Предупреждение';
    }
  }

  Color _getNotificationColor(NotificationType type) {
    switch (type) {
      case NotificationType.info:
        return const Color(0xFF2196F3);
      case NotificationType.attention:
        return const Color(0xFFFF9800);
      case NotificationType.warning:
        return Colors.red;
    }
  }

  void _initScooterIcon() async {
    await ClusterIconPainter.initImage('assets/icons/scooter_placemark.png');
  }

  List<PlacemarkMapObject> _buildScooterPlacemarks({
    required List<Scooter> scooters,
    required String iconAsset,
    required bool isClickable,
  }) {
    return scooters.map((scooter) {
      return PlacemarkMapObject(
        mapId: MapObjectId('${isClickable ? "" : "reserved_"}${scooter.id}'), // уникальный ID для карты
        point: Point(latitude: scooter.longitude, longitude: scooter.latitude),
        icon: PlacemarkIcon.single(
          PlacemarkIconStyle(
            image: BitmapDescriptor.fromAssetImage(iconAsset),
            scale: 0.2,
          ),
        ),
        opacity: 1.0,
        consumeTapEvents: isClickable,
        onTap: isClickable
            ? (object, point) async => _onMarkerTap([scooter])
            : null,
      );
    }).toList();
  }

  List<MapObject> _buildZonePolygons(List<Zone>? zones) {
    if (zones == null || zones.isEmpty) return [];

    List<MapObject> objects = [];

    List<LinearRing> allZoneHoles = [];

    for (var zone in zones) {
      var points = zone.points.map((p) => Point(latitude: p.latitude, longitude: p.longitude)).toList();

      var cleanPoints = <Point>[];
      for (var p in points) {
        if (cleanPoints.isEmpty || cleanPoints.last != p) {
          cleanPoints.add(p);
        }
      }

      if (cleanPoints.length > 2) {
        if (cleanPoints.first != cleanPoints.last) {
          cleanPoints.add(cleanPoints.first);
        }

        allZoneHoles.add(LinearRing(points: cleanPoints));
      }
    }

    objects.add(
      PolygonMapObject(
        mapId: const MapObjectId('global_inverse_mask'),
        polygon: Polygon(
          outerRing: const LinearRing(points: [
            Point(latitude: 85, longitude: -179.9),
            Point(latitude: 85, longitude: 179.9),
            Point(latitude: -85, longitude: 179.9),
            Point(latitude: -85, longitude: -179.9),
          ]),
          innerRings: allZoneHoles,
        ),
        strokeWidth: 0,
        fillColor: Colors.red.withOpacity(0.15),
        zIndex: 0,
      ),
    );

    for (var zone in zones) {
      Color borderColor;
      if (zone.type == "Drive") {
        borderColor = const Color(0xFF5ECD4C);
      } else if (zone.type == "NotDrive") {
        borderColor = const Color(0xFFEF4444);
      } else {
        borderColor = const Color(0xFFA78BFA);
      }

      objects.add(
        PolylineMapObject(
          mapId: MapObjectId('zone_contour_${zone.id}'),
          polyline: Polyline(
            points: zone.points.map((p) => Point(latitude: p.latitude, longitude: p.longitude)).toList(),
          ),
          strokeColor: borderColor,
          strokeWidth: 2.0,
          zIndex: 1,
        ),
      );
    }

    return objects;
  }

  Widget _buildTopButtons() {
    return Stack(
      children: [
        Positioned(
          top: 16,
          left: 16,
          child: Builder(
            builder: (innerContext) => _RoundIconButton(
              icon: Icons.menu,
              onPressed: () => {Scaffold.of(innerContext).openDrawer()},
            ),
          ),
        ),
        Positioned(
          top: 16,
          right: 16,
          child: Column(
            children: [
              _RoundIconButton(
                icon: Icons.notifications_sharp,
                onPressed: () => context.push("/home/notifications"),
              ),
              const SizedBox(height: 12),
              _RoundButton(
                imagePath: 'assets/icons/scooter_placemark.png',
                onPressed: () => context.push("/home/current-rides-sheet"),
              )
            ],
          ),
        ),
      ],
    );
  }

  Widget _buildCentralQrButton() {
    return Positioned(
      bottom: 24,
      left: 0,
      right: 0,
      child: Center(
        child: GestureDetector(
          onTap: () {
            final flags = context.read<MapBloc>().state.flags;

            // 🔹 Проверка флагов — показываем те же карточки, что и в listener
            if (!flags.hasCard) {
              _showExistingNotification(
                child: PaymentNotificationCard(
                  onBindCard: () {
                    BotToast.cleanAll();
                    context.push("/home/payment-methods");
                  },
                  onClose: () => BotToast.cleanAll(),
                ),
              );
              return;
            }
            if (flags.hasUnpaidOrder) {
              _showExistingNotification(
                child: UnpaidOrderNotificationCard(
                  onClose: () => BotToast.cleanAll(),
                ),
              );
              return;
            }
            if (flags.hasFine) {
              _showExistingNotification(
                child: FineNotificationCard(
                  onClose: () => BotToast.cleanAll(),
                ),
              );
              return;
            }

            // ✅ Все проверки пройдены — переход на сканирование
            context.push("/home/qr-info");
          },
          child: Container(
            width: 64,
            height: 64,
            decoration: BoxDecoration(
              color: AppColors.darkBlue,
              shape: BoxShape.circle,
              boxShadow: [
                BoxShadow(
                  color: Colors.black.withOpacity(0.3),
                  blurRadius: 10,
                  offset: const Offset(0, 4),
                ),
              ],
            ),
            child: const Icon(
              Icons.qr_code_scanner,
              color: Colors.white,
              size: 32,
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildSideControls() {
    return Positioned(
      right: 16,
      top: 0,
      bottom: 0,
      child: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            _CircleIconButton(icon: Icons.map, onPressed: _onMapSettingsTap),
            const SizedBox(height: 16),
            _CircleIconButton(
              icon: Icons.my_location,
              onPressed: () {
                context.read<MapBloc>().add(
                  UpdateUserLocation(
                    _currentPosition!.latitude,
                    _currentPosition!.longitude,
                  ),
                );
                _moveCameraToPoint(
                  _currentPosition!.latitude,
                  _currentPosition!.longitude,
                  zoom: 17,
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

Future<Uint8List> painterToBytes(CustomPainter painter, Size size) async {
  final recorder = ui.PictureRecorder();
  final canvas = Canvas(recorder);
  painter.paint(canvas, size);
  final picture = recorder.endRecording();
  final image = await picture.toImage(size.width.toInt(), size.height.toInt());
  final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
  return byteData!.buffer.asUint8List();
}

class _RoundIconButton extends StatelessWidget {
  final IconData icon;
  final VoidCallback onPressed;

  const _RoundIconButton({required this.icon, required this.onPressed});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 40,
      height: 40,
      decoration: BoxDecoration(
        color: AppColors.darkBlue,
        borderRadius: BorderRadius.circular(12),
      ),
      child: IconButton(
        icon: Icon(icon, color: Colors.white, size: 24),
        onPressed: onPressed,
      ),
    );
  }
}

class _RoundButton extends StatelessWidget {
  final String imagePath;
  final VoidCallback onPressed;

  const _RoundButton({
    required this.imagePath,
    required this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 40,
      height: 40,
      decoration: BoxDecoration(
        color: AppColors.darkBlue,
        borderRadius: BorderRadius.circular(12),
      ),
      child: GestureDetector(
        onTap: onPressed,
        child: Center(
          child: Image.asset(
            imagePath,
            width: 20,
            height: 20,
            fit: BoxFit.contain,
            color: Colors.white,
          ),
        ),
      ),
    );
  }
}

class _CircleIconButton extends StatelessWidget {
  final IconData icon;
  final VoidCallback onPressed;

  const _CircleIconButton({required this.icon, required this.onPressed});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 40,
      height: 40,
      decoration: const BoxDecoration(
        color: AppColors.darkBlue,
        shape: BoxShape.circle,
      ),
      child: IconButton(
        icon: Icon(icon, color: Colors.white, size: 24),
        onPressed: onPressed,
      ),
    );
  }
}

void _showExistingNotification({required Widget child}) {
  BotToast.showCustomNotification(
    duration: null, // не исчезает, пока пользователь не закроет
    toastBuilder: (_) {
      return Container(
        margin: const EdgeInsets.only(top: 120), // тот же отступ, что в listener
        child: Material(
          color: Colors.transparent,
          child: child, // PaymentNotificationCard / UnpaidOrderNotificationCard / FineNotificationCard
        ),
      );
    },
  );
}
