import 'dart:async';
import 'package:dio/dio.dart';

/// Deduplicates identical in-flight GET requests.
/// If the same GET URL is requested while a previous request is still pending,
/// the second request reuses the result of the first instead of hitting
/// the server again. This prevents thundering herd on page loads and
/// rapid retry-button clicks.
class DedupInterceptor extends Interceptor {
  /// Stores both the result completer and a listener count.
  /// When count drops to 0 on error, we avoid completing an orphan completer.
  final _pending = <String, _PendingEntry>{};

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    // Only dedup GET requests (mutations must always go through).
    // Skip dedup for retry requests (they already failed once).
    if (options.method.toUpperCase() != 'GET' ||
        (options.extra['_retryCount'] as int? ?? 0) > 0) {
      return handler.next(options);
    }

    final key = '${options.method}:${options.uri}';

    final entry = _pending[key];
    if (entry != null) {
      // Another identical request is in flight — piggyback on it.
      entry.listeners++;
      entry.completer.future.then(
        (response) => handler.resolve(
          Response(
            requestOptions: options,
            data: response.data,
            statusCode: response.statusCode,
            headers: response.headers,
          ),
        ),
        onError: (Object e) {
          if (e is DioException) {
            handler.reject(DioException(
              requestOptions: options,
              type: e.type,
              error: e.error,
              message: e.message,
              response: e.response,
            ));
          } else {
            handler.reject(DioException(
              requestOptions: options,
              error: e,
            ));
          }
        },
      );
      return;
    }

    // First request for this key — register and proceed.
    _pending[key] = _PendingEntry(Completer<Response>());
    handler.next(options);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    _complete(response.requestOptions, response: response);
    handler.next(response);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    _complete(err.requestOptions, error: err);
    handler.next(err);
  }

  void _complete(RequestOptions options, {Response? response, DioException? error}) {
    final key = '${options.method}:${options.uri}';
    final entry = _pending.remove(key);
    if (entry == null || entry.completer.isCompleted) return;

    if (response != null) {
      entry.completer.complete(response);
    } else if (error != null) {
      entry.completer.completeError(error);
    }
  }
}

class _PendingEntry {
  final Completer<Response> completer;
  int listeners;
  _PendingEntry(this.completer, {this.listeners = 0});
}
