import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

typedef NavResult<T> = void Function(T? result);
typedef NavOpened = void Function();

class AppNavigator {
  AppNavigator._();

  static Future<T?> push<T extends Object?>(
    BuildContext context,
    String route, {
    Object? extra,
    NavOpened? onOpened,
    NavResult<T>? onResult,
  }) async {
    final future = context.push<T>(route, extra: extra);

    if (onOpened != null) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        onOpened();
      });
    }

    if (onResult != null) {
      await future.then(onResult);
    }

    return future;
  }

  static Future<void> clearAndPush(
    BuildContext context,
    String route, {
    Object? extra,
    NavOpened? onOpened,
  }) async {
    context.go(route, extra: extra);

    if (onOpened != null) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        onOpened();
      });
    }
  }

  static void pop<T extends Object?>(
    BuildContext context, [
    T? result,
  ]) {
    context.pop(result);
  }

  static void popToRoot(BuildContext context) {
    while (context.canPop()) {
      context.pop();
    }
  }

  static Future<bool> maybePop(BuildContext context) async {
    if (context.canPop()) {
      context.pop();
      return true;
    }
    return false;
  }

  static Future<T?> pushFade<T extends Object?>(
    BuildContext context,
    String route, {
    Object? extra,
    Duration duration = const Duration(milliseconds: 200),
    NavOpened? onOpened,
    NavResult<T>? onResult,
  }) async {
    final future = context.push<T>(
      route,
      extra: extra,
    );

    if (onOpened != null) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        onOpened();
      });
    }

    if (onResult != null) {
      await future.then(onResult);
    }

    return future;
  }

  static Future<T?> pushSlide<T extends Object?>(
    BuildContext context,
    String route, {
    Object? extra,
    Duration duration = const Duration(milliseconds: 250),
    NavOpened? onOpened,
    NavResult<T>? onResult,
  }) async {
    final future = context.push<T>(
      route,
      extra: extra,
    );

    if (onOpened != null) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        onOpened();
      });
    }

    if (onResult != null) {
      await future.then(onResult);
    }

    return future;
  }
}
