import 'package:flutter_test/flutter_test.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';
import 'package:travel_mate/blocs/auth/auth_bloc.dart';
import 'package:travel_mate/blocs/auth/auth_event.dart';
import 'package:travel_mate/blocs/auth/auth_state.dart';
import 'package:travel_mate/repositories/auth_repository.dart';
import 'package:travel_mate/models/user.dart';
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;

import 'package:travel_mate/services/notification_service.dart';

import 'auth_bloc_test.mocks.dart';

@GenerateMocks([AuthRepository, NotificationService])
void main() {
  group('AuthBloc', () {
    late MockAuthRepository mockAuthRepository;
    late MockNotificationService mockNotificationService;
    late AuthBloc authBloc;

    final user = User(
      id: '123',
      nom: 'Doe',
      prenom: 'John',
      email: 'test@example.com',
      platform: 'email',
    );

    setUp(() {
      mockAuthRepository = MockAuthRepository();
      mockNotificationService = MockNotificationService();
      authBloc = AuthBloc(
        authRepository: mockAuthRepository,
        notificationService: mockNotificationService,
      );

      // Default stub for saveTokenToFirestore to avoid strict mock errors
      when(
        mockNotificationService.saveTokenToFirestore(any),
      ).thenAnswer((_) async {});
    });

    tearDown(() {
      authBloc.close();
    });

    test('initial state is AuthUninitialized', () {
      expect(authBloc.state, AuthInitial());
    });

    blocTest<AuthBloc, AuthState>(
      'emits [AuthAuthenticated] when AuthCheckRequested is added and user is logged in',
      build: () {
        when(
          mockAuthRepository.currentUser,
        ).thenReturn(MockFirebaseUser(uid: '123', email: 'test@example.com'));
        when(
          mockAuthRepository.getUserFromFirestore('123'),
        ).thenAnswer((_) async => user);
        return authBloc;
      },
      act: (bloc) => bloc.add(AuthCheckRequested()),
      expect: () => [AuthLoading(), AuthAuthenticated(user: user)],
    );

    blocTest<AuthBloc, AuthState>(
      'emits [AuthUnauthenticated] when AuthCheckRequested is added and user is not logged in',
      build: () {
        when(mockAuthRepository.currentUser).thenReturn(null);
        return authBloc;
      },
      act: (bloc) => bloc.add(AuthCheckRequested()),
      expect: () => [AuthLoading(), AuthUnauthenticated()],
    );

    blocTest<AuthBloc, AuthState>(
      'emits [AuthAuthenticated] when AuthSignInRequested is added',
      build: () {
        when(
          mockAuthRepository.signInWithEmailAndPassword(
            email: 'test@example.com',
            password: 'password',
          ),
        ).thenAnswer((_) async => user);
        return authBloc;
      },
      act: (bloc) => bloc.add(
        const AuthSignInRequested(
          email: 'test@example.com',
          password: 'password',
        ),
      ),
      expect: () => [AuthLoading(), AuthAuthenticated(user: user)],
    );

    blocTest<AuthBloc, AuthState>(
      'emits [AuthUnauthenticated] when AuthSignOutRequested is added',
      build: () {
        when(mockAuthRepository.signOut()).thenAnswer((_) async {});
        return authBloc;
      },
      act: (bloc) => bloc.add(AuthSignOutRequested()),
      expect: () => [AuthUnauthenticated()],
      verify: (_) {
        verify(mockAuthRepository.signOut()).called(1);
      },
    );
  });
}

// Simple Mock for FirebaseUser since we can't easily mock the real one without more boilerplate
// or using firebase_auth_mocks package which we didn't add.
// However, AuthRepository.currentUser returns firebase_auth.User.
// Attempting to mock it via extends might be tricky due to private constructors.
// Let's rely on Mockito to generate a mock for firebase_auth.User if needed,
// or adjusting the test to not depend on the return value of currentUser being a complex object
// if the Bloc only checks for null.
//
// Looking at AuthBloc source (I haven't read it yet), it probably checks `authRepository.currentUser`.
// I'll read AuthBloc code in the next step to be sure how to mock the return value.
class MockFirebaseUser extends Mock implements firebase_auth.User {
  @override
  final String uid;
  @override
  final String? email;

  MockFirebaseUser({required this.uid, this.email});
}
