import 'dart:async';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:mypoint_flutter_app/shared/widgets/loading/app_loading.dart';
import '../../../app/routing/app_navigator.dart';
import '../error_mapper.dart';
import '../dio_http_service.dart';


class ExceptionInterceptor extends Interceptor {
  static Completer<bool>? _currentRetryPrompt;
  static bool _isPrompting = false;

  @override
  Future<void> onError(DioException err, ErrorInterceptorHandler handler) async {
    final apiError = ErrorMapper.map(err);
    err.requestOptions.extra['mapped_error'] = apiError;
    // return handler.next(err);
    debugPrint('ExceptionInterceptor: onError: $apiError');
    final extra = err.requestOptions.extra;
    final silent = extra['silent'] == true;
    // Nếu không phải network error hoặc không thể retry -> forward
    if (!ErrorMapper.isNetworkError(err)) return handler.next(err);
    if (silent) return handler.next(err);
    debugPrint('ExceptionInterceptor: onError: $apiError');
    // Chỉ cho phép retry với GET hoặc request được mark allow_retry
    final allowRetry = _shouldAllowRetry(err.requestOptions);
    if (!allowRetry) return handler.next(err);
    debugPrint('ExceptionInterceptor: onError: $_isPrompting');
    if (_isPrompting) return handler.next(err);
    _isPrompting = true;
    // ask user retry
    final shouldRetry = await _askUserRetry(apiError);
    if (!shouldRetry) {
      _isPrompting = false;
      return handler.next(err);
    }
    // retry
    try {
      final response = await _retryRequest(err.requestOptions);
      return handler.resolve(response);
    } catch (retryError) {
      final retryException = retryError is DioException
          ? retryError
          : DioException(requestOptions: err.requestOptions, error: retryError);
      handler.reject(retryException);
    } finally {
      _isPrompting = false;
    }
  }

  /// Kiểm tra xem request có được phép retry không
  bool _shouldAllowRetry(RequestOptions options) {
    // GET request luôn được phép retry
    if (options.method.toUpperCase() == 'GET') return true;

    // Request được mark explicitly allow retry
    if (options.extra['allow_retry'] == true) return true;

    // Idempotent methods
    const idempotentMethods = ['GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS'];
    return idempotentMethods.contains(options.method.toUpperCase());
  }

  /// Hỏi user có muốn retry không (throttle để tránh multiple popups)
  Future<bool> _askUserRetry(String message) async {
    AppLoading().hide();
    final existing = _currentRetryPrompt;
    if (existing != null) {
      return existing.future;
    }
    final completer = Completer<bool>();
    _currentRetryPrompt = completer;
    try {
      AppNavigator.showNoInternetAlert(
        message,
        () => _completeRetryPrompt(completer, true),  // Retry
        () => _completeRetryPrompt(completer, false), // Close
      );
      return await completer.future;
    } finally {
      _currentRetryPrompt = null;
    }
  }

  /// Complete retry prompt safely
  void _completeRetryPrompt(Completer<bool> completer, bool result) {
    if (!completer.isCompleted) {
      completer.complete(result);
    }
  }

  /// Retry request với options mới
  Future<Response<dynamic>> _retryRequest(RequestOptions originalOptions) {
    final retryOptions = Options(
      method: originalOptions.method,
      headers: Map<String, dynamic>.from(originalOptions.headers),
      responseType: originalOptions.responseType,
      contentType: originalOptions.contentType,
      sendTimeout: originalOptions.sendTimeout,
      receiveTimeout: originalOptions.receiveTimeout,
      followRedirects: originalOptions.followRedirects,
      listFormat: originalOptions.listFormat,
      validateStatus: originalOptions.validateStatus,
      extra: {
        ...originalOptions.extra,
        'silent': true,
        'allow_retry': false,// Silent để không show popup lặp
      },
    );

    return DioHttpService().dio.requestUri<dynamic>(
      originalOptions.uri,
      data: originalOptions.data,
      options: retryOptions,
      cancelToken: originalOptions.cancelToken,
      onReceiveProgress: originalOptions.onReceiveProgress,
      onSendProgress: originalOptions.onSendProgress,
    );
  }
}
