import 'package:dio/dio.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

import 'package:superport_v2/core/network/interceptors/auth_interceptor.dart';
import 'package:superport_v2/core/services/token_storage.dart';

class _MockTokenStorage extends Mock implements TokenStorage {}

class _MockDio extends Mock implements Dio {}

class _CapturingErrorHandler extends ErrorInterceptorHandler {
  Response<dynamic>? resolved;
  DioException? forwarded;

  @override
  void resolve(Response<dynamic> response) {
    resolved = response;
  }

  @override
  void next(DioException err) {
    forwarded = err;
  }
}

void main() {
  setUpAll(() {
    registerFallbackValue(RequestOptions(path: '/fallback'));
  });

  group('AuthInterceptor', () {
    late TokenStorage storage;
    late Dio dio;

    setUp(() {
      storage = _MockTokenStorage();
      dio = _MockDio();
      when(() => storage.writeAccessToken(any())).thenAnswer((_) async {});
      when(() => storage.writeRefreshToken(any())).thenAnswer((_) async {});
      when(() => storage.clear()).thenAnswer((_) async {});
    });

    test('401 응답 시 토큰을 갱신하고 요청을 재시도한다', () async {
      when(
        () => storage.readAccessToken(),
      ).thenAnswer((_) async => 'renewed-access');
      when(() => dio.fetch<dynamic>(any())).thenAnswer((invocation) async {
        final options = invocation.positionalArguments.first as RequestOptions;
        return Response<dynamic>(
          requestOptions: options,
          statusCode: 200,
          data: {'ok': true},
        );
      });

      final interceptor = AuthInterceptor(
        tokenStorage: storage,
        dio: dio,
        onRefresh: () async => const TokenPair(
          accessToken: 'renewed-access',
          refreshToken: 'renewed-refresh',
        ),
      );

      final requestOptions = RequestOptions(path: '/approvals');
      requestOptions.headers['Authorization'] = 'Bearer legacy-token';
      final error = DioException(
        requestOptions: requestOptions,
        response: Response<dynamic>(
          requestOptions: requestOptions,
          statusCode: 401,
        ),
        type: DioExceptionType.badResponse,
      );

      final handler = _CapturingErrorHandler();
      await interceptor.onError(error, handler);

      expect(handler.forwarded, isNull);
      expect(handler.resolved, isNotNull);
      expect(
        requestOptions.headers['Authorization'],
        equals('Bearer renewed-access'),
      );
      verify(() => storage.writeAccessToken('renewed-access')).called(1);
      verify(() => storage.writeRefreshToken('renewed-refresh')).called(1);
      verifyNever(() => storage.clear());
      verify(() => dio.fetch<dynamic>(any())).called(1);
    });

    test('토큰 갱신 실패 시 저장소를 초기화하고 오류를 전달한다', () async {
      when(
        () => storage.readAccessToken(),
      ).thenAnswer((_) async => 'legacy-token');

      final interceptor = AuthInterceptor(
        tokenStorage: storage,
        dio: dio,
        onRefresh: () async => null,
      );

      final requestOptions = RequestOptions(path: '/approvals');
      final error = DioException(
        requestOptions: requestOptions,
        response: Response<dynamic>(
          requestOptions: requestOptions,
          statusCode: 401,
        ),
        type: DioExceptionType.badResponse,
      );

      final handler = _CapturingErrorHandler();
      await interceptor.onError(error, handler);

      expect(handler.resolved, isNull);
      expect(handler.forwarded, same(error));
      verify(() => storage.clear()).called(1);
      verifyNever(() => dio.fetch<dynamic>(any()));
    });
  });
}
