import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:lanebow/services/member_service.dart';
import 'package:lanebow/services/api_service.dart';
import 'package:lanebow/services/event_bus.dart'; // EventBus().fire() 호출에 필요
import 'package:lanebow/config/api_config.dart';

// 모킹 클래스 생성
@GenerateMocks([ApiService])
import 'member_service_test.mocks.dart';

void main() {
  late MemberService memberService;
  late MockApiService mockApiService;
  final testToken = 'test_token';
  final testClubId = 'test_club_id';
  final testMemberId = 'test_member_id';

  // 테스트 데이터 초기화
  final testMember = {
    'id': 'test_member_id',
    'name': '테스트 회원',
    'email': 'test@example.com',
    'phone': '010-1234-5678',
    'gender': '남성',
    'birthYear': 1990,
    'level': '중급',
    'position': '회원',
    'joinDate': DateTime.now().toIso8601String(),
    'clubId': 'test_club_id',
    'isActive': true,
  };

  setUp(() async {
    // SharedPreferences 모킹 설정 - 모든 테스트에서 사용
    SharedPreferences.setMockInitialValues({
      ApiConfig.tokenKey: testToken,
      ApiConfig.clubIdKey: 'initial_club_id',
    });

    // API 서비스 모킹
    mockApiService = MockApiService();
    when(mockApiService.setToken(testToken)).thenReturn(null);
    
    // 기본 API 호출 모킹 설정 - 모든 테스트에서 필요한 fetchClubMembers 설정
    when(mockApiService.post(
      '${ApiConfig.clubs}/members',
      data: {'clubId': testClubId},
    )).thenAnswer((_) async => [testMember]);
    
    // 초기 클럽 ID에 대한 fetchClubMembers 호출 모킹 - 모든 테스트에서 필요
    when(mockApiService.post(
      '${ApiConfig.clubs}/members',
      data: {'clubId': 'initial_club_id'},
    )).thenAnswer((_) async => [testMember]);
    
    // 새 클럽 ID에 대한 fetchClubMembers 호출 모킹 - 이벤트 테스트에서 필요
    when(mockApiService.post(
      '${ApiConfig.clubs}/members',
      data: {'clubId': 'new_club_id'},
    )).thenAnswer((_) async => [testMember]);
    
    // MemberService 초기화 (테스트용 생성자 사용)
    memberService = MemberService.forTest(mockApiService);
    
    // 토큰 설정
    memberService.initialize(testToken);
    
    // 클럽 ID 설정 - 각 테스트에서 필요한 경우 직접 호출하도록 변경
    // await memberService.setClubId(testClubId);
    
    // 모든 호출 기록 초기화 - 각 테스트가 독립적으로 실행되도록 함
    clearInteractions(mockApiService);
  });

  group('MemberService 테스트', () {
    test('initialize 메서드가 토큰을 설정해야 함', () async {
      // given
      final mockApiService = MockApiService();
      when(mockApiService.setToken(testToken)).thenReturn(null);
      
      // SharedPreferences 모킹 설정
      SharedPreferences.setMockInitialValues({
        ApiConfig.tokenKey: testToken,
        ApiConfig.clubIdKey: 'initial_club_id',
      });
      
      // 초기 클럽 ID에 대한 fetchClubMembers 호출 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': 'initial_club_id'},
      )).thenAnswer((_) async => [testMember]);
      
      // 새 클럽 ID에 대한 fetchClubMembers 호출 모킹 - 이벤트 테스트에서 필요
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': 'new_club_id'},
      )).thenAnswer((_) async => [testMember]);
      
      // when
      final memberService = MemberService.forTest(mockApiService);
      memberService.initialize(testToken);
      
      // 이벤트 처리를 위한 지연 - 더 긴 시간으로 설정
      await Future.delayed(Duration(milliseconds: 500));
      
      // then
      verify(mockApiService.setToken(testToken)).called(1);
    });

    test('setClubId 메서드가 클럽 ID를 설정하고 회원 목록을 가져와야 함', () async {
      // given
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => [testMember]);

      // when
      await memberService.setClubId(testClubId);

      // then
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': testClubId},
      )).called(1);
    });

    test('fetchClubMembers 메서드가 클럽의 회원 목록을 가져와야 함', () async {
      // given
      // 클럽 ID 설정 - 필수
      await memberService.setClubId(testClubId);
      
      // 초기화 후 모든 호출 기록 초기화
      clearInteractions(mockApiService);
      
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => [testMember]);

      // when
      await memberService.fetchClubMembers();

      // then
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': testClubId},
      )).called(1);
      
      expect(memberService.members.length, 1);
      expect(memberService.members[0].id, testMemberId);
      expect(memberService.members[0].name, '테스트 회원');
    });

    test('fetchClubMembers \uba54\uc11c\ub4dc\uac00 \uac1d\uccb4 \ud615\ud0dc\uc758 \uc751\ub2f5\ub3c4 \ucc98\ub9ac\ud574\uc57c \ud568', () async {
      // given
      // \ud074\ub7fd ID \uc124\uc815 - \ud544\uc218
      await memberService.setClubId(testClubId);
      
      // 초기화 후 모든 호출 기록 초기화
      clearInteractions(mockApiService);
      
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => {'members': [testMember]});

      // when
      await memberService.fetchClubMembers();

      // then
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': testClubId},
      )).called(1);
      
      expect(memberService.members.length, 1);
      expect(memberService.members[0].id, testMemberId);
    });

    test('fetchMemberById 메서드가 특정 회원 정보를 가져와야 함', () async {
      // given
      // 클럽 ID 설정 - 필수
      await memberService.setClubId(testClubId);
      
      when(mockApiService.post(
        '${ApiConfig.clubs}/members/detail',
        data: {'memberId': testMemberId},
      )).thenAnswer((_) async => {
        'statusCode': 200,
        'member': testMember,
      });

      // when
      final result = await memberService.fetchMemberById(testMemberId);

      // then
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members/detail',
        data: {'memberId': testMemberId},
      )).called(1);
      
      expect(result.id, testMemberId);
      expect(result.name, '테스트 회원');
    });

    test('addMember 메서드가 새 회원을 추가해야 함', () async {
      // given
      // API 서비스 모킹 생성
      final mockApiService = MockApiService();
      when(mockApiService.setToken(testToken)).thenReturn(null);
      
      // SharedPreferences 모킹 설정
      SharedPreferences.setMockInitialValues({
        ApiConfig.tokenKey: testToken,
        ApiConfig.clubIdKey: testClubId,
      });
      
      // 클럽 회원 목록 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => [testMember]);
      
      // 이벤트 발생 시 new_club_id에 대한 모킹 추가
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': 'new_club_id'},
      )).thenAnswer((_) async => [testMember]);
      
      // MemberService 생성 및 초기화
      final memberService = MemberService.forTest(mockApiService);
      memberService.initialize(testToken);
      
      // 클럽 ID 설정
      await memberService.setClubId(testClubId);
      
      // 초기화 후 모든 호출 기록 초기화
      clearInteractions(mockApiService);
      
      final newMemberData = {
        'name': '새 회원',
        'email': 'new@example.com',
        'phone': '010-9876-5432',
        'clubId': testClubId,
      };
      
      when(mockApiService.post(
        '${ApiConfig.clubs}/members/add',
        data: newMemberData,
      )).thenAnswer((_) async => {'member': testMember});

      // when
      final result = await memberService.addMember(newMemberData);

      // then
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members/add',
        data: newMemberData,
      )).called(1);
      
      expect(result.id, testMemberId);
    });

    test('addMember 메서드가 직접 회원 객체가 반환되는 경우도 처리해야 함', () async {
      // given
      // API 서비스 모킹 생성
      final mockApiService = MockApiService();
      when(mockApiService.setToken(testToken)).thenReturn(null);
      
      // SharedPreferences 모킹 설정
      SharedPreferences.setMockInitialValues({
        ApiConfig.tokenKey: testToken,
        ApiConfig.clubIdKey: testClubId,
      });
      
      // 클럽 회원 목록 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => [testMember]);
      
      // 이벤트 발생 시 new_club_id에 대한 모킹 추가
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': 'new_club_id'},
      )).thenAnswer((_) async => [testMember]);
      
      // MemberService 생성 및 초기화
      final memberService = MemberService.forTest(mockApiService);
      memberService.initialize(testToken);
      
      // 클럽 ID 설정
      await memberService.setClubId(testClubId);
      
      // 초기화 후 모든 호출 기록 초기화
      clearInteractions(mockApiService);
      
      final newMemberData = {
        'name': '새 회원',
        'email': 'new@example.com',
        'phone': '010-9876-5432',
        'clubId': testClubId,
      };
      
      // 직접 회원 객체 반환 시ミュレイション
      when(mockApiService.post(
        '${ApiConfig.clubs}/members/add',
        data: newMemberData,
      )).thenAnswer((_) async => testMember);

      // when
      final result = await memberService.addMember(newMemberData);

      // then
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members/add',
        data: newMemberData,
      )).called(1);
      
      expect(result.id, testMemberId);
      expect(memberService.members.length, 1);
      expect(memberService.members[0].id, testMemberId);
    test('updateMember 메서드가 회원 정보를 업데이트해야 함', () async {
      // given
      // API 서비스 모킹 생성
      final mockApiService = MockApiService();
      when(mockApiService.setToken(testToken)).thenReturn(null);
      
      // SharedPreferences 모킹 설정
      SharedPreferences.setMockInitialValues({
        ApiConfig.tokenKey: testToken,
        ApiConfig.clubIdKey: testClubId,
      });
      
      // 클럽 회원 목록 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => [testMember]);
      
      // 이벤트 발생 시 new_club_id에 대한 모킹 추가
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': 'new_club_id'},
      )).thenAnswer((_) async => [testMember]);
      
      // MemberService 생성 및 초기화
      final memberService = MemberService.forTest(mockApiService);
      memberService.initialize(testToken);
      
      // 클럽 ID 설정
      await memberService.setClubId(testClubId);
      
      // 먼저 회원 목록 가져오기
      await memberService.fetchClubMembers();
      
      // 초기화 후 모든 호출 기록 초기화
      clearInteractions(mockApiService);
      
      final updates = {
        'name': '업데이트된 회원',
      };
      
      when(mockApiService.post(
        '${ApiConfig.clubs}/members/update',
        data: {'clubId': testClubId, 'memberId': testMemberId, ...updates},
      )).thenAnswer((_) async => {
        'member': {
          ...testMember,
          'name': '업데이트된 회원',
        }
      });

      // when
      final result = await memberService.updateMember(testMemberId, updates);

      // then
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members/update',
        data: {'clubId': testClubId, 'memberId': testMemberId, ...updates},
      )).called(1);
      
      expect(result.name, '업데이트된 회원');
      expect(memberService.members[0].name, '업데이트된 회원');
    });

    test('updateMember 메서드가 직접 회원 객체가 반환되는 경우도 처리해야 함', () async {
      // given
      // API 서비스 모킹 생성
      final mockApiService = MockApiService();
      when(mockApiService.setToken(testToken)).thenReturn(null);
      
      // SharedPreferences 모킹 설정
      SharedPreferences.setMockInitialValues({
        ApiConfig.tokenKey: testToken,
        ApiConfig.clubIdKey: testClubId,
      });
      
      // 클럽 회원 목록 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => [testMember]);
      
      // 이벤트 발생 시 new_club_id에 대한 모킹 추가
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': 'new_club_id'},
      )).thenAnswer((_) async => [testMember]);
      
      // MemberService 생성 및 초기화
      final memberService = MemberService.forTest(mockApiService);
      memberService.initialize(testToken);
      
      // 클럽 ID 설정
      await memberService.setClubId(testClubId);
      
      // 먼저 회원 목록 가져오기
      await memberService.fetchClubMembers();
      
      final updates = {'name': '업데이트된 회원'};
      final updatedMember = Map<String, dynamic>.from(testMember);
      updatedMember['name'] = '업데이트된 회원';
      
      // 직접 회원 객체 반환 시ミ레이션
      when(mockApiService.post(
        '${ApiConfig.clubs}/members/update',
        data: {'clubId': testClubId, 'memberId': testMemberId, ...updates},
      )).thenAnswer((_) async => updatedMember);

      // when
      final result = await memberService.updateMember(testMemberId, updates);

      // then
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members/update',
        data: {'clubId': testClubId, 'memberId': testMemberId, ...updates},
      )).called(1);
      
      expect(result.name, '업데이트된 회원');
      expect(memberService.members[0].name, '업데이트된 회원');
    });

    test('deleteMember 메서드가 회원을 삭제해야 함', () async {
      // given
      // API 서비스 모킹 생성
      final mockApiService = MockApiService();
      when(mockApiService.setToken(testToken)).thenReturn(null);
      
      // SharedPreferences 모킹 설정
      SharedPreferences.setMockInitialValues({
        ApiConfig.tokenKey: testToken,
        ApiConfig.clubIdKey: testClubId,
      });
      
      // 클럽 회원 목록 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => [testMember]);
      
      // 이벤트 발생 시 new_club_id에 대한 모킹 추가
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': 'new_club_id'},
      )).thenAnswer((_) async => [testMember]);
      
      // MemberService 생성 및 초기화
      final memberService = MemberService.forTest(mockApiService);
      memberService.initialize(testToken);
      
      // 클럽 ID 설정
      await memberService.setClubId(testClubId);
      
      // 먼저 회원 목록 가져오기
      await memberService.fetchClubMembers();
      
      when(mockApiService.post(
        '${ApiConfig.clubs}/members/delete',
        data: {'memberId': testMemberId, 'clubId': testClubId},
      )).thenAnswer((_) async => {'success': true});

      // when
      final result = await memberService.deleteMember(testMemberId);

      // then
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members/delete',
        data: {'memberId': testMemberId, 'clubId': testClubId},
      )).called(1);
      
      expect(result, true);
      expect(memberService.members.length, 0);
    });

    test('ClubChangedEvent가 발생하면 setClubId가 호출되어야 함', () async {
      // given
      final initialClubId = 'initial_club_id';
      final newClubId = 'new_club_id';
      
      // API 서비스 모킹 생성
      final mockApiService = MockApiService();
      
      // SharedPreferences 모킹 설정
      SharedPreferences.setMockInitialValues({
        ApiConfig.clubIdKey: initialClubId,
        ApiConfig.tokenKey: testToken,
      });
      
      // 토큰 설정
      when(mockApiService.setToken(testToken)).thenReturn(null);
      
      // 초기 클럽 ID에 대한 호출 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': initialClubId},
      )).thenAnswer((_) async => [testMember]);
      
      // 새 클럽 ID에 대한 호출 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/members',
        data: {'clubId': newClubId},
      )).thenAnswer((_) async => [testMember]);
      
      // 어떤 인자로든 post 호출에 대한 기본 응답 설정 - 명명된 매개변수에 대한 올바른 mockito 문법 사용
      when(mockApiService.post(
        any,
        data: anyNamed('data'),
      )).thenAnswer((_) async => [testMember]);
      
      // MemberService 생성 및 초기화
      final memberService = MemberService.forTest(mockApiService);
      memberService.initialize(testToken);
      
      // 초기화 후 지연 추가
      await Future.delayed(Duration(milliseconds: 500));
      
      // 초기 클럽 ID로 회원 목록 가져오기
      await memberService.fetchClubMembers();
      
      // 초기화 후 모든 호출 확인 초기화
      clearInteractions(mockApiService);
      
      // when - setClubId 호출
      await memberService.setClubId(newClubId);
      
      // 비동기 처리를 위한 지연 추가
      await Future.delayed(Duration(milliseconds: 500));
      
      // then - 실제 MemberService에서 API 호출 확인
      // setClubId 호출 후 fetchClubMembers가 호출되어야 함
      verify(mockApiService.post(
        any,
        data: anyNamed('data'),
      )).called(greaterThanOrEqualTo(1));
    });
  });
}
