import 'dart:async';

import 'package:dio/dio.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:rasadyar_core/infrastructure/remote/app_interceptor_n.dart';

class MockDio extends Mock implements Dio {}

class MockResponse extends Mock implements Response {}

class MockRequestOptions extends Mock implements RequestOptions {}


class FakeRequestOptions extends Fake implements RequestOptions {}


void main() {
  late MockDio dio;
  late AppInterceptorN interceptor;
  late MockResponse response;
  late MockRequestOptions requestOptions;

  late bool saveCalled;
  late bool clearCalled;
  late bool refreshFailedCalled;
  String? savedToken;

  setUp(() {
    dio = MockDio();
    response = MockResponse();
    requestOptions = MockRequestOptions();

    saveCalled = false;
    clearCalled = false;
    refreshFailedCalled = false;
    savedToken = null;
    interceptor = AppInterceptorN(
      dio: dio,
      saveTokenCallback: (token) async {
        savedToken = token;
        saveCalled = true;
      },
      clearTokenCallback: () async {
        clearCalled = true;
      },
      refreshTokenCallback: () async => 'newToken',
      onRefreshFailed: () async {
        refreshFailedCalled = true;
      },
    );
  });

  setUpAll(() {
    registerFallbackValue(FakeRequestOptions());
  });



  test('should refresh token and retry queued requests on 401', () async {
    final options = RequestOptions(path: "/test");
    final response = Response(requestOptions: options, statusCode: 200, data: 'ok');
    when(() => dio.fetch(any())).thenAnswer((_) async => response);
    final handler = ErrorInterceptorHandler();
    final dioException = DioException(
      requestOptions: options,
      response: Response(requestOptions: options, statusCode: 401),
      type: DioExceptionType.badResponse,
    );
    await interceptor.onError(dioException, handler);
    expect(saveCalled, isTrue);
    expect(savedToken, 'newToken');
    expect(interceptor.queue.isEmpty, isTrue);
    expect(clearCalled, isFalse);
    expect(refreshFailedCalled, isFalse);
  });


  test('should queue request if refreshing', () async {
    interceptor.isRefreshingForTest = true;
    final options = RequestOptions(path: "/test");
    final handler = RequestInterceptorHandler();
    final completer = Completer<Response>();
    interceptor.queue.add(QueuedRequest(options, completer));
    interceptor.onRequest(options, handler);
    expect(interceptor.queue.length, 2); // One added in setUp, one here
  });

  test('should refresh token and retry queued requests on 401', () async {
    final options = RequestOptions(path: "/test");
    final response = Response(requestOptions: options, statusCode: 200, data: 'ok');
    when(() => dio.fetch(any())).thenAnswer((_) async => response);
    final handler = ErrorInterceptorHandler();
    final dioException = DioException(
      requestOptions: options,
      response: Response(requestOptions: options, statusCode: 401),
      type: DioExceptionType.badResponse,
    );
    await interceptor.onError(dioException, handler);
    expect(saveCalled, isTrue);
    expect(savedToken, 'newToken');
    expect(interceptor.queue.isEmpty, isTrue);
    expect(clearCalled, isFalse);
    expect(refreshFailedCalled, isFalse);
  });
}
