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/services/api_service.dart';
import 'package:lanebow/services/score_service.dart';
import 'package:lanebow/services/event_bus.dart';
import 'package:lanebow/utils/test_utils.dart';

import 'score_service_integration_test.mocks.dart';

@GenerateMocks([ApiService])
void main() {
  late ScoreService scoreService;
  late MockApiService mockApiService;
  final testToken = 'test_token';
  final testClubId = 'test_club_id';
  final testMemberId = 'test_member_id';
  final testEventId = 'test_event_id';
  final testScoreId = 'test_score_id';

  // 테스트 점수 데이터
  final testScore = {
    'id': 'test_score_id',
    'memberId': 'test_member_id',
    'eventId': 'test_event_id',
    'clubId': 'test_club_id',
    'frames': [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
    'totalScore': 55,
    'date': DateTime.now().toIso8601String(),
    'notes': '테스트 점수',
  };

  // 테스트 통계 데이터
  final testStatistics = {
    'average': 150.5,
    'highScore': 200,
    'lowScore': 100,
    'gamesPlayed': 10,
    'additionalStats': {
      'strikes': 5,
      'spares': 3,
    },
  };

  setUp(() async {
    TestWidgetsFlutterBinding.ensureInitialized();
    
    // 테스트 격리를 위한 설정
    final testId = 'score_service_integration_test_${DateTime.now().millisecondsSinceEpoch}';
    TestUtils.setTestMode(true);
    EventBus.setCurrentTestId(testId);
    
    print('EventBus: 초기화됨 (테스트 ID: $testId)');
    
    // SharedPreferences 모킹
    SharedPreferences.setMockInitialValues({
      ApiConfig.clubIdKey: testClubId,
      ApiConfig.tokenKey: testToken
    });

    // API 서비스 모킹
    mockApiService = MockApiService();
    
    // 모든 테스트에서 공통으로 필요한 모킹 설정
    when(mockApiService.setToken(any)).thenReturn(null);
    
    // 클럽 점수 목록 조회 모킹
    when(mockApiService.post(
      '${ApiConfig.clubs}/scores',
      data: {'clubId': testClubId},
    )).thenAnswer((_) async => [testScore]);
    
    // ScoreService 초기화 (forTest 생성자 사용)
    scoreService = ScoreService.forTest(mockApiService, clubId: testClubId);
  });

  group('ScoreService 통합 테스트', () {
    test('점수 목록 조회 테스트', () async {
      // given
      const token = 'test_token';
      
      // API 응답 모킹 - 상세 정보 추가
      when(mockApiService.post(
        '${ApiConfig.clubs}/scores',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => [
        {
          'id': 'score_id_1',
          'memberId': 'member_id_1',
          'eventId': 'event_id_1',
          'clubId': testClubId,
          'frames': [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
          'totalScore': 55,
          'date': '2023-01-01T12:00:00Z',
          'notes': '테스트 점수 1',
        },
        {
          'id': 'score_id_2',
          'memberId': 'member_id_2',
          'eventId': 'event_id_1',
          'clubId': testClubId,
          'frames': [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
          'totalScore': 45,
          'date': '2023-01-02T12:00:00Z',
          'notes': '테스트 점수 2',
        }
      ]);
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);
      
      // 토큰 설정
      scoreService.initialize(token);
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);

      // when
      await scoreService.fetchClubScores();

      // then
      expect(scoreService.scores.length, 2);
      expect(scoreService.scores[0].id, 'score_id_1');
      expect(scoreService.scores[0].totalScore, 55);
      expect(scoreService.scores[1].id, 'score_id_2');
      expect(scoreService.scores[1].totalScore, 45);
      
      verify(mockApiService.post(
        '${ApiConfig.clubs}/scores',
        data: {'clubId': testClubId},
      )).called(1);
    });

    test('회원별 점수 조회 테스트', () async {
      // given
      const token = 'test_token';
      const memberId = 'test_member_id';
      
      // API 응답 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/members/scores',
        data: {'memberId': memberId},
      )).thenAnswer((_) async => {
        'scores': [
          {
            'id': 'score_id_1',
            'memberId': memberId,
            'eventId': 'event_id_1',
            'clubId': testClubId,
            'frames': [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
            'totalScore': 55,
            'date': '2023-01-01T12:00:00Z',
            'notes': '회원 테스트 점수 1',
          },
          {
            'id': 'score_id_3',
            'memberId': memberId,
            'eventId': 'event_id_2',
            'clubId': testClubId,
            'frames': [8, 7, 6, 5, 4, 3, 2, 1, 0, 0],
            'totalScore': 36,
            'date': '2023-01-03T12:00:00Z',
            'notes': '회원 테스트 점수 2',
          }
        ]
      });
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);
      
      // 토큰 설정
      scoreService.initialize(token);
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);

      // when
      final result = await scoreService.fetchMemberScores(memberId);

      // then
      expect(result.length, 2);
      expect(result[0].id, 'score_id_1');
      expect(result[0].totalScore, 55);
      expect(result[1].id, 'score_id_3');
      expect(result[1].totalScore, 36);
      
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members/scores',
        data: {'memberId': memberId},
      )).called(1);
    });

    test('이벤트별 점수 조회 테스트', () async {
      // given
      const token = 'test_token';
      const eventId = 'test_event_id';
      
      // API 응답 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/events/scores',
        data: {'eventId': eventId},
      )).thenAnswer((_) async => {
        'scores': [
          {
            'id': 'score_id_1',
            'memberId': 'member_id_1',
            'eventId': eventId,
            'clubId': testClubId,
            'frames': [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
            'totalScore': 55,
            'date': '2023-01-01T12:00:00Z',
            'notes': '이벤트 테스트 점수 1',
          },
          {
            'id': 'score_id_2',
            'memberId': 'member_id_2',
            'eventId': eventId,
            'clubId': testClubId,
            'frames': [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
            'totalScore': 45,
            'date': '2023-01-02T12:00:00Z',
            'notes': '이벤트 테스트 점수 2',
          }
        ]
      });
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);
      
      // 토큰 설정
      scoreService.initialize(token);
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);

      // when
      final result = await scoreService.fetchEventScores(eventId);

      // then
      expect(result.length, 2);
      expect(result[0].id, 'score_id_1');
      expect(result[0].totalScore, 55);
      expect(result[1].id, 'score_id_2');
      expect(result[1].totalScore, 45);
      
      verify(mockApiService.post(
        '${ApiConfig.clubs}/events/scores',
        data: {'eventId': eventId},
      )).called(1);
    });

    test('점수 추가 테스트', () async {
      // given
      const token = 'test_token';
      
      final newScoreData = {
        'memberId': testMemberId,
        'eventId': testEventId,
        'clubId': testClubId,
        'frames': [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
        'totalScore': 55,
      };
      
      // API 응답 모킹
      when(mockApiService.post(
        ApiConfig.scores,
        data: newScoreData,
      )).thenAnswer((_) async => {'score': testScore});
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);
      
      // 토큰 설정
      scoreService.initialize(token);
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);

      // when
      final result = await scoreService.addScore(newScoreData);

      // then
      expect(result.id, testScoreId);
      expect(result.totalScore, 55);
      expect(scoreService.scores.length, 1);
      expect(scoreService.scores[0].id, testScoreId);
      
      verify(mockApiService.post(
        ApiConfig.scores,
        data: newScoreData,
      )).called(1);
    });

    test('점수 수정 테스트', () async {
      // given
      const token = 'test_token';
      const scoreId = 'test_score_id';
      
      // 먼저 점수 목록 가져오기
      when(mockApiService.post(
        '${ApiConfig.clubs}/scores',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => [testScore]);
      
      // 토큰 설정
      scoreService.initialize(token);
      await scoreService.fetchClubScores();
      
      final updates = {'totalScore': 60};
      final updatedScore = Map<String, dynamic>.from(testScore);
      updatedScore['totalScore'] = 60;
      
      // API 응답 모킹
      when(mockApiService.put(
        '${ApiConfig.scores}/$scoreId',
        data: updates,
      )).thenAnswer((_) async => {'score': updatedScore});
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);

      // when
      final result = await scoreService.updateScore(scoreId, updates);

      // then
      expect(result.totalScore, 60);
      expect(scoreService.scores[0].totalScore, 60);
      
      verify(mockApiService.put(
        '${ApiConfig.scores}/$scoreId',
        data: updates,
      )).called(1);
    });

    test('점수 삭제 테스트', () async {
      // given
      const token = 'test_token';
      const scoreId = 'test_score_id';
      
      // 먼저 점수 목록 가져오기
      when(mockApiService.post(
        '${ApiConfig.clubs}/scores',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => [testScore]);
      
      // 토큰 설정
      scoreService.initialize(token);
      await scoreService.fetchClubScores();
      
      // API 응답 모킹
      when(mockApiService.delete(
        '${ApiConfig.scores}/$scoreId',
      )).thenAnswer((_) async => {'success': true});
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);

      // when
      final result = await scoreService.deleteScore(scoreId);

      // then
      expect(result, true);
      expect(scoreService.scores.length, 0);
      
      verify(mockApiService.delete(
        '${ApiConfig.scores}/$scoreId',
      )).called(1);
    });

    test('회원 통계 조회 테스트', () async {
      // given
      const token = 'test_token';
      const memberId = 'test_member_id';
      
      // API 응답 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/members/stats',
        data: {'memberId': memberId},
      )).thenAnswer((_) async => {'statistics': testStatistics});
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);
      
      // 토큰 설정
      scoreService.initialize(token);
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);

      // when
      final result = await scoreService.fetchMemberStatistics(memberId);

      // then
      expect(result.average, 150.5);
      expect(result.highScore, 200);
      expect(result.lowScore, 100);
      expect(result.gamesPlayed, 10);
      expect(result.additionalStats?['strikes'], 5);
      expect(result.additionalStats?['spares'], 3);
      
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members/stats',
        data: {'memberId': memberId},
      )).called(1);
    });

    test('클럽 통계 조회 테스트', () async {
      // given
      const token = 'test_token';
      
      final clubStats = {
        'overall': testStatistics,
        'monthly': testStatistics,
      };
      
      // API 응답 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/scores/stats',
        data: {'clubId': testClubId},
      )).thenAnswer((_) async => {'statistics': clubStats});
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);
      
      // 토큰 설정
      scoreService.initialize(token);
      
      // 모든 호출 기록 초기화
      clearInteractions(mockApiService);

      // when
      final result = await scoreService.fetchClubStatistics();

      // then
      expect(result.length, 2);
      expect(result['overall']?.average, 150.5);
      expect(result['monthly']?.highScore, 200);
      
      verify(mockApiService.post(
        '${ApiConfig.clubs}/scores/stats',
        data: {'clubId': testClubId},
      )).called(1);
    });
  });
  
  group('에러 처리 테스트', () {
    test('API 에러 처리 테스트', () async {
      // given
      const token = 'test_token';
      const memberId = 'test_member_id';
      
      // 토큰 설정
      scoreService.initialize(token);
      
      // API 에러 모킹
      when(mockApiService.post(
        '${ApiConfig.clubs}/members/stats',
        data: {'memberId': memberId},
      )).thenThrow(Exception('API 에러 발생'));

      // when & then
      expect(
        () => scoreService.fetchMemberStatistics(memberId),
        throwsA(isA<Exception>()),
      );
      
      verify(mockApiService.post(
        '${ApiConfig.clubs}/members/stats',
        data: {'memberId': memberId},
      )).called(1);
    });
    
    test('토큰 미설정 테스트', () async {
      // given
      // 토큰 설정 없이 새 서비스 인스턴스 생성
      mockApiService = MockApiService();
      scoreService = ScoreService.forTest(mockApiService, clubId: testClubId);

      // when & then
      expect(
        () => scoreService.fetchClubScores(),
        throwsA(isA<Exception>()),
      );
    });
    
    test('클럽 ID 미설정 테스트', () async {
      // given
      const token = 'test_token';
      
      // 클럽 ID 없이 새 서비스 인스턴스 생성
      mockApiService = MockApiService();
      scoreService = ScoreService.forTest(mockApiService);
      
      // 토큰만 설정
      scoreService.initialize(token);

      // when & then
      expect(
        () => scoreService.fetchMemberScores(testMemberId),
        throwsA(isA<Exception>()),
      );
    });
  });
  
  tearDown(() {
    // 테스트 종료 후 정리
    scoreService.dispose();
    EventBus.clearCurrentTestId();
    TestUtils.setTestMode(false);
    print('EventBus: 테스트 종료');
  });
}
