import 'dart:async';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../configs/constants.dart';

class AppLoading {
  // Singleton ẩn
  static final AppLoading _i = AppLoading._();
  factory AppLoading() => _i;
  AppLoading._();

  // Truyền key này cho GetMaterialApp (navigatorKey: Get.key)
  // hoặc tự tạo GlobalKey<NavigatorState> riêng rồi thay vào đây.
  OverlayState? _overlay;        // cache sau khi app sẵn sàng
  OverlayEntry? _entry;
  Timer? _timer;

  // Hàng đợi thao tác (insert/remove)
  final Queue<void Function()> _ops = Queue();
  bool _flushScheduled = false;
  bool get isShowing => _entry != null;

  /// Gọi 1 lần sau runApp để cache OverlayState.
  void attach() {
    if (_overlay != null) return;
    // Lấy overlay ở post-frame để tránh visitChildElements trong build
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _overlay = Get.key.currentState?.overlay
          ?? Overlay.of(Get.overlayContext ?? Get.context!, rootOverlay: true);
      _scheduleFlush(); // có gì trong hàng đợi thì flush
    });
  }

  /// Chỉ schedule flush khi framework rảnh (sau frame + microtask tiếp theo).
  void _scheduleFlush() {
    if (_flushScheduled) return;
    _flushScheduled = true;

    // Đẩy sang post-frame
    WidgetsBinding.instance.addPostFrameCallback((_) {
      // Và tiếp tục đẩy sang event-loop tiếp theo để chắc chắn đã qua build
      Future.microtask(() {
        _flushScheduled = false;

        // Nếu overlay chưa sẵn, đợi frame sau nữa
        if (_overlay == null) {
          attach();
          return;
        }

        while (_ops.isNotEmpty) {
          final op = _ops.removeFirst();
          op();
        }
      });
    });
  }

  void show({
    Duration timeout = const Duration(seconds: Constants.timeoutSeconds),
    Color? barrierColor = const Color(0x33000000),
    bool absorbPointers = true,
    double size = 56,
    double strokeWidth = 4,
  }) {
    debugPrint('AppLoading.show called');
    // Đưa thao tác vào hàng đợi, không làm ngay
    _ops.add(() {
      if (isShowing) {
        _timer?.cancel();
        _timer = Timer(timeout, hide);
        return;
      }

      final entry = OverlayEntry(
        builder: (_) => Stack(
          fit: StackFit.expand,
          children: [
            if (barrierColor != null)
              const SizedBox.expand(
                child: IgnorePointer(ignoring: true, child: SizedBox()),
              ),
            if (barrierColor != null)
              ModalBarrier(color: barrierColor, dismissible: false),
            IgnorePointer(
              ignoring: !absorbPointers,
              child: Center(
                child: SizedBox(
                  width: size,
                  height: size,
                  child: CircularProgressIndicator(strokeWidth: strokeWidth),
                ),
              ),
            ),
          ],
        ),
      );

      _overlay!.insert(entry);
      _entry = entry;

      _timer?.cancel();
      _timer = Timer(timeout, hide);
    });

    attach();
    _scheduleFlush();
  }

  void hide() {
    debugPrint('AppLoading.hide called');
    _ops.add(() {
      _timer?.cancel();
      _timer = null;

      _entry?.remove();
      _entry = null;
    });

    attach();
    _scheduleFlush();
  }
}
