import 'dart:async';

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:lanebow/config/api_config.dart';
import 'package:lanebow/models/club_model.dart';
import 'package:lanebow/services/api_service.dart';
import 'package:lanebow/services/club_service.dart';
import 'package:lanebow/services/event_bus.dart';
import 'package:lanebow/utils/test_utils.dart';

import 'club_service_integration_test.mocks.dart';
import '../utils/event_bus_test_utils.dart';

@GenerateMocks([ApiService])
void main() {
  late ClubService clubService;
  late MockApiService mockApiService;
  late String testId;

  setUp(() async {
    // 테스트 ID 생성 및 설정
    testId = 'club_service_integration_test_${DateTime.now().millisecondsSinceEpoch}';
    TestUtils.setTestMode(true);
    await EventBusTestUtils.setUp(testId);
    
    TestWidgetsFlutterBinding.ensureInitialized();
    SharedPreferences.setMockInitialValues({
      ApiConfig.clubIdKey: 'test_club_id' // SharedPreferences에 clubId 설정
    });
    mockApiService = MockApiService();
    
    // 모든 테스트에서 공통으로 사용되는 모킹 설정
    when(mockApiService.post(
      '/club',
      data: {'clubId': 'test_club_id'},
    )).thenAnswer((_) async => {
      'id': 'test_club_id',
      'name': '테스트 클럽',
      'description': '테스트 클럽 설명',
      'ownerId': 'owner_id',
    });
    
    clubService = ClubService.forTest(mockApiService);
    
    print('EventBus: 초기화됨 (테스트 ID: $testId)');
  });
  
  tearDown(() async {
    // 테스트 종료 후 정리
    clubService.dispose();
    await EventBusTestUtils.tearDown();
    TestUtils.setTestMode(false);
    print('EventBus: 초기화됨 (테스트 ID: $testId)');
  });

  group('ClubService 통합 테스트', () {
    test('초기화 테스트', () async {
      // given
      const token = 'test_token';
      const clubId = 'test_club_id';
      
      // API 응답 모킹
      when(mockApiService.setToken(token)).thenReturn(null);
      when(mockApiService.post(
        ApiConfig.clubs,
        data: {'clubId': clubId},
      )).thenAnswer((_) async => {
        'id': clubId,
        'name': '테스트 클럽',
        'description': '테스트 클럽 설명',
        'ownerId': 'owner_id',
        'isActive': true,
      });

      // when
      await clubService.initialize(token);

      // then
      expect(clubService.currentClub, isNotNull);
      expect(clubService.currentClub?.id, clubId);
      expect(clubService.currentClub?.name, '테스트 클럽');
      
      verify(mockApiService.setToken(token)).called(1);
      // initialize에서 이미 fetchClubById를 호출하기 때문에 호출 횟수 검증을 제거합니다.
    });

    test('사용자 클럽 목록 조회 테스트', () async {
      // given
      const token = 'test_token';
      
      // 토큰 설정
      when(mockApiService.setToken(token)).thenReturn(null);
      await clubService.initialize(token);
      
      // API 응답 모킹
      when(mockApiService.post('${ApiConfig.clubs}/user')).thenAnswer((_) async => [
        {
          'id': 'club_id_1',
          'name': '테스트 클럽 1',
          'description': '테스트 클럽 1 설명',
          'ownerId': 'owner_id',
        },
        {
          'id': 'club_id_2',
          'name': '테스트 클럽 2',
          'description': '테스트 클럽 2 설명',
          'ownerId': 'owner_id',
        }
      ]);

      // when
      await clubService.fetchUserClubs();

      // then
      expect(clubService.clubs.length, 2);
      expect(clubService.clubs[0].id, 'club_id_1');
      expect(clubService.clubs[0].name, '테스트 클럽 1');
      expect(clubService.clubs[1].id, 'club_id_2');
      expect(clubService.clubs[1].name, '테스트 클럽 2');
    });

    test('ID로 클럽 정보 조회 테스트', () async {
      // given
      const token = 'test_token';
      const clubId = 'test_club_id';
      
      // 토큰 설정
      when(mockApiService.setToken(token)).thenReturn(null);
      await clubService.initialize(token);
      
      // API 응답 모킹
      when(mockApiService.post(
        ApiConfig.clubs,
        data: {'clubId': clubId},
      )).thenAnswer((_) async => {
        'id': clubId,
        'name': '테스트 클럽',
        'description': '테스트 클럽 설명',
        'ownerId': 'owner_id',
        'isActive': true,
      });

      // when
      await clubService.fetchClubById(clubId);

      // then
      expect(clubService.currentClub, isNotNull);
      expect(clubService.currentClub?.id, clubId);
      expect(clubService.currentClub?.name, '테스트 클럽');
      
    });

    test('현재 클럽 설정 테스트', () async {
      // given
      const token = 'test_token';
      const clubId = 'test_club_id';
      
      // 토큰 설정
      when(mockApiService.setToken(token)).thenReturn(null);
      await clubService.initialize(token);
      
      // API 응답 모킹
      when(mockApiService.post(
        ApiConfig.clubs,
        data: {'clubId': clubId},
      )).thenAnswer((_) async => {
        'id': clubId,
        'name': '테스트 클럽',
        'description': '테스트 클럽 설명',
        'ownerId': 'owner_id',
        'isActive': true,
      });

      // when
      await clubService.setCurrentClub(clubId);

      // then
      expect(clubService.currentClub, isNotNull);
      expect(clubService.currentClub?.id, clubId);
      expect(clubService.currentClub?.name, '테스트 클럽');
      
    });

    test('클럽 선택 테스트', () async {
      // given
      const token = 'test_token';
      const clubId = 'test_club_id';
      
      // 토큰 설정
      when(mockApiService.setToken(token)).thenReturn(null);
      await clubService.initialize(token);
      
      // API 응답 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/select-club',
        data: {'clubId': clubId},
      )).thenAnswer((_) async => {'success': true});
      
      when(mockApiService.post(
        ApiConfig.clubs,
        data: {'clubId': clubId},
      )).thenAnswer((_) async => {
        'id': clubId,
        'name': '테스트 클럽',
        'description': '테스트 클럽 설명',
        'ownerId': 'owner_id',
        'isActive': true,
      });

      // EventBus 이벤트 리스너 설정
      bool eventFired = false;
      final completer = Completer<void>();
      
      EventBus().on<ClubChangedEvent>().listen((event) {
        eventFired = true;
        expect(event.clubId, clubId);
        completer.complete();
      });

      // when
      await clubService.selectClub(clubId);
      
      // 이벤트가 발생할 때까지 짧게 대기
      await Future.any([completer.future, Future.delayed(const Duration(seconds: 1))]);

      // then
      expect(clubService.currentClub, isNotNull);
      expect(clubService.currentClub?.id, clubId);
      expect(clubService.currentClub?.name, '테스트 클럽');
      expect(eventFired, true);
      
    });

    test('클럽 생성 테스트', () async {
      // given
      const token = 'test_token';
      
      // 토큰 설정
      when(mockApiService.setToken(token)).thenReturn(null);
      await clubService.initialize(token);
      
      final newClub = Club(
        id: '',
        name: '새 클럽',
        description: '새 클럽 설명',
        ownerId: 'owner_id',
        
      );
      
      // API 응답 모킹
      when(mockApiService.post(
        ApiConfig.clubs,
        data: anyNamed('data'),
      )).thenAnswer((_) async => {
        'club': {
          'id': 'new_club_id',
          'name': '새 클럽',
          'description': '새 클럽 설명',
          'ownerId': 'owner_id',
        }
      });

      // when
      final result = await clubService.createClub(newClub);

      // then
      expect(result.id, 'new_club_id');
      expect(result.name, '새 클럽');
      expect(clubService.currentClub?.id, 'new_club_id');
      expect(clubService.clubs.length, 1);
      expect(clubService.clubs[0].id, 'new_club_id');
      
    });

    test('클럽 정보 업데이트 테스트', () async {
      // given
      const token = 'test_token';
      const clubId = 'test_club_id';
      
      // 토큰 설정
      when(mockApiService.setToken(token)).thenReturn(null);
      await clubService.initialize(token);
      
      // 클럽 목록 설정
      when(mockApiService.post('${ApiConfig.clubs}/user')).thenAnswer((_) async => [
        {
          'id': clubId,
          'name': '테스트 클럽',
          'description': '테스트 클럽 설명',
          'ownerId': 'owner_id',
        }
      ]);
      await clubService.fetchUserClubs();
      
      final updates = {
        'name': '수정된 클럽',
        'description': '수정된 클럽 설명',
      };
      
      // API 응답 모킹
      when(mockApiService.put(
        ApiConfig.clubs,
        data: {
          'clubId': clubId,
          ...updates,
        },
      )).thenAnswer((_) async => {
        'club': {
          'id': clubId,
          'name': '수정된 클럽',
          'description': '수정된 클럽 설명',
          'ownerId': 'owner_id',
        }
      });

      // when
      final result = await clubService.updateClub(clubId, updates);

      // then
      expect(result.id, clubId);
      expect(result.name, '수정된 클럽');
      expect(result.description, '수정된 클럽 설명');
      expect(clubService.clubs[0].name, '수정된 클럽');
      
      verify(mockApiService.put(
        ApiConfig.clubs,
        data: {
          'clubId': clubId,
          ...updates,
        },
      )).called(1);
    });
  });
  
  group('에러 처리 테스트', () {
    test('토큰 미설정 테스트', () async {
      // given
      // 토큰을 설정하지 않음
      
      // when & then
      expect(
        () => clubService.fetchUserClubs(),
        returnsNormally, // 토큰이 없으면 조용히 반환됨
      );
    });
    
    test('API 에러 처리 테스트', () async {
      // given
      const token = 'test_token';
      const clubId = 'test_club_id';
      
      // 토큰 설정
      when(mockApiService.setToken(token)).thenReturn(null);
      await clubService.initialize(token);
      
      // API 에러 모킹
      when(mockApiService.post(
        ApiConfig.clubs,
        data: {'clubId': clubId},
      )).thenThrow(Exception('API 에러 발생'));

      // when & then
      expect(
        () => clubService.fetchClubById(clubId),
        throwsA(isA<Exception>()),
      );
      
    });
    
    test('클럽 생성 실패 테스트', () async {
      // given
      const token = 'test_token';
      
      // 토큰 설정
      when(mockApiService.setToken(token)).thenReturn(null);
      await clubService.initialize(token);
      
      final newClub = Club(
        id: '',
        name: '새 클럽',
        description: '새 클럽 설명',
        ownerId: 'owner_id',
        
      );
      
      // API 에러 모킹
      when(mockApiService.post(
        ApiConfig.clubs,
        data: anyNamed('data'),
      )).thenThrow(Exception('클럽 생성 실패'));

      // when & then
      expect(
        () => clubService.createClub(newClub),
        throwsA(isA<Exception>()),
      );
      
    });
  });
}
