import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:mockito/mockito.dart';
import 'package:fotodocumentation/utils/di_container.dart';
import 'package:fotodocumentation/utils/http_client_interceptor.dart';
import 'package:fotodocumentation/utils/login_credentials.dart';

import '../testing/test_utils.mocks.dart';

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  group('HttpClientInterceptor', () {
    late MockClient mockInnerClient;
    late MockLoginCredentials mockLoginCredentials;
    late HttpClientInterceptor interceptingClient;

    setUp(() {
      DiContainer.instance.initState();

      mockInnerClient = MockClient();
      mockLoginCredentials = MockLoginCredentials();

      DiContainer.instance.put(LoginCredentials, mockLoginCredentials);

      interceptingClient = HttpClientInterceptor(mockInnerClient);
    });

    test('calls logout on 401 response', () async {
      // Mock 401 response
      final request = http.Request('GET', Uri.parse('http://example.com/api/test'));
      final streamedResponse = http.StreamedResponse(
        Stream.value([]),
        401,
        headers: {'content-type': 'application/json'},
      );

      when(mockInnerClient.send(any)).thenAnswer((_) async => streamedResponse);

      // Execute request
      final response = await interceptingClient.send(request);

      // Verify logout was called
      verify(mockLoginCredentials.logout()).called(1);

      // Verify response is still returned
      expect(response.statusCode, 401);
    });

    test('does not interfere with successful responses', () async {
      // Mock 200 response
      final request = http.Request('GET', Uri.parse('http://example.com/api/test'));
      final streamedResponse = http.StreamedResponse(
        Stream.value([]),
        200,
        headers: {'content-type': 'application/json'},
      );

      when(mockInnerClient.send(any)).thenAnswer((_) async => streamedResponse);

      // Execute request
      final response = await interceptingClient.send(request);

      // Verify response is passed through
      expect(response.statusCode, 200);

      // Verify logout was NOT called
      verifyNever(mockLoginCredentials.logout());
    });

    test('does not interfere with 404 responses', () async {
      // Mock 404 response
      final request = http.Request('GET', Uri.parse('http://example.com/api/test'));
      final streamedResponse = http.StreamedResponse(
        Stream.value([]),
        404,
        headers: {'content-type': 'application/json'},
      );

      when(mockInnerClient.send(any)).thenAnswer((_) async => streamedResponse);

      // Execute request
      final response = await interceptingClient.send(request);

      // Verify response is passed through
      expect(response.statusCode, 404);

      // Verify logout was NOT called
      verifyNever(mockLoginCredentials.logout());
    });

    test('does not interfere with 500 responses', () async {
      // Mock 500 response
      final request = http.Request('GET', Uri.parse('http://example.com/api/test'));
      final streamedResponse = http.StreamedResponse(
        Stream.value([]),
        500,
        headers: {'content-type': 'application/json'},
      );

      when(mockInnerClient.send(any)).thenAnswer((_) async => streamedResponse);

      // Execute request
      final response = await interceptingClient.send(request);

      // Verify response is passed through
      expect(response.statusCode, 500);

      // Verify logout was NOT called
      verifyNever(mockLoginCredentials.logout());
    });

    test('handles multiple 401 responses gracefully', () async {
      // Mock 401 response
      final request1 = http.Request('GET', Uri.parse('http://example.com/api/test1'));
      final request2 = http.Request('GET', Uri.parse('http://example.com/api/test2'));
      final streamedResponse = http.StreamedResponse(
        Stream.value([]),
        401,
        headers: {'content-type': 'application/json'},
      );

      when(mockInnerClient.send(any)).thenAnswer((_) async => streamedResponse);

      // Execute multiple requests
      await interceptingClient.send(request1);
      await interceptingClient.send(request2);

      // Verify logout was called for each 401
      verify(mockLoginCredentials.logout()).called(2);
    });
  });
}