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/group/group_bloc.dart';
import 'package:travel_mate/blocs/group/group_event.dart';
import 'package:travel_mate/blocs/group/group_state.dart';
import 'package:travel_mate/repositories/group_repository.dart';
import 'package:travel_mate/models/group.dart';
import 'package:travel_mate/models/group_member.dart';

import 'group_bloc_test.mocks.dart';

@GenerateMocks([GroupRepository])
void main() {
  group('GroupBloc', () {
    late MockGroupRepository mockGroupRepository;
    late GroupBloc groupBloc;

    final group = Group(
      id: 'group1',
      name: 'Test Group',
      tripId: 'trip1',
      createdBy: 'user1',
    );

    setUp(() {
      mockGroupRepository = MockGroupRepository();
      groupBloc = GroupBloc(mockGroupRepository);
    });

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

    test('initial state is GroupInitial', () {
      expect(groupBloc.state, GroupInitial());
    });

    // LoadGroupsByUserId - Stream test
    // Stream mocking is a bit verbose with Mockito. We simulate stream behavior.
    blocTest<GroupBloc, GroupState>(
      'emits [GroupLoading, GroupsLoaded] when LoadGroupsByUserId is added',
      setUp: () {
        when(
          mockGroupRepository.getGroupsByUserId('user1'),
        ).thenAnswer((_) => Stream.value([group]));
      },
      build: () => groupBloc,
      act: (bloc) => bloc.add(LoadGroupsByUserId('user1')),
      expect: () => [
        GroupLoading(),
        GroupsLoaded([group]),
      ],
    );

    blocTest<GroupBloc, GroupState>(
      'emits [GroupLoading, GroupError] when LoadGroupsByUserId stream errors',
      setUp: () {
        when(
          mockGroupRepository.getGroupsByUserId('user1'),
        ).thenAnswer((_) => Stream.error('Error loading groups'));
      },
      build: () => groupBloc,
      act: (bloc) => bloc.add(LoadGroupsByUserId('user1')),
      expect: () => [GroupLoading(), GroupError('Error loading groups')],
    );

    // CreateGroup
    blocTest<GroupBloc, GroupState>(
      'emits [GroupLoading, GroupCreated, GroupOperationSuccess] when CreateGroup is added',
      setUp: () {
        when(
          mockGroupRepository.createGroupWithMembers(
            group: anyNamed('group'),
            members: anyNamed('members'),
          ),
        ).thenAnswer((_) async => 'group1');
      },
      build: () => groupBloc,
      act: (bloc) => bloc.add(CreateGroup(group)),
      expect: () => [
        GroupLoading(),
        GroupCreated(groupId: 'group1'),
        GroupOperationSuccess('Group created successfully'),
      ],
    );

    // AddMemberToGroup
    final member = GroupMember(
      userId: 'user2',
      firstName: 'Bob',
      role: 'member',
      joinedAt: DateTime.now(),
    );
    blocTest<GroupBloc, GroupState>(
      'emits [GroupOperationSuccess] when AddMemberToGroup is added',
      setUp: () {
        when(
          mockGroupRepository.addMember('group1', member),
        ).thenAnswer((_) async {});
      },
      build: () => groupBloc,
      act: (bloc) => bloc.add(AddMemberToGroup('group1', member)),
      expect: () => [GroupOperationSuccess('Member added')],
    );
  });
}
