import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';
import 'package:http/http.dart' as http;
import 'package:lanebow/services/subscription_service.dart';
import 'package:lanebow/services/in_app_purchase_service.dart';
import 'package:lanebow/utils/test_utils.dart';
import '../utils/event_bus_test_utils.dart';

// 모킹 클래스 생성
@GenerateMocks([http.Client, InAppPurchaseService])
import 'subscription_service_test.mocks.dart';

void main() {
  late SubscriptionService subscriptionService;
  late MockClient mockClient;
  late MockInAppPurchaseService mockPurchaseService;
  final testToken = 'test_token';
  final testClubId = 'test_club_id';
  final testUserId = 'test_user_id';

  // 테스트 구독 데이터
  final testSubscription = {
    'id': 'test_subscription_id',
    'userId': testUserId,
    'clubId': testClubId,
    'planType': 'premium',
    'status': 'active',
    'startDate': DateTime.now().toIso8601String(),
    'endDate': DateTime.now().add(Duration(days: 30)).toIso8601String(),
    'price': 19900.0,
    'currency': 'KRW',
    'autoRenew': true,
    'features': {
      'maxMembers': 100,
      'maxEvents': 100,
    },
    'paymentMethod': 'card',
    'transactionId': 'test_transaction_id',
    'createdAt': DateTime.now().subtract(Duration(days: 10)).toIso8601String(),
    'updatedAt': DateTime.now().toIso8601String(),
  };

  // 테스트 구독 플랜 데이터
  final testPlans = [
    {
      'type': 'basic',
      'name': '기본',
      'description': '중소규모 클럽을 위한 확장 기능',
      'monthlyPrice': 9900.0,
      'yearlyPrice': 99000.0,
      'currency': 'KRW',
      'features': [
        '최대 30명 회원 관리',
        '상세 점수 분석',
        '이벤트 관리',
        '회원 통계',
      ],
      'maxMembers': 30,
      'maxEvents': 20,
      'hasAdvancedStats': true,
      'hasCustomization': false,
      'hasPriority': false,
    },
    {
      'type': 'premium',
      'name': '프리미엄',
      'description': '대규모 클럽을 위한 고급 기능',
      'monthlyPrice': 19900.0,
      'yearlyPrice': 199000.0,
      'currency': 'KRW',
      'features': [
        '무제한 회원 관리',
        '고급 통계 및 분석',
        '무제한 이벤트',
        '맞춤형 보고서',
        '우선 지원',
      ],
      'maxMembers': 100,
      'maxEvents': 100,
      'hasAdvancedStats': true,
      'hasCustomization': true,
      'hasPriority': true,
    }
  ];

  setUp(() async {
    // 테스트 모드 강제 설정
    TestUtils.setTestMode(true);
    
    // 테스트별 고유 ID 생성 및 설정
    final testId = 'subscription_service_test_${DateTime.now().millisecondsSinceEpoch}';
    await EventBusTestUtils.setUp(testId);
    
    // HTTP 클라이언트 모킹
    mockClient = MockClient();
    
    // InAppPurchaseService 모킹
    mockPurchaseService = MockInAppPurchaseService();
    
    // HTTP 클라이언트 요청 모킹
    when(mockClient.post(
      any,
      headers: anyNamed('headers'),
      body: anyNamed('body'),
    )).thenAnswer((_) async => http.Response(jsonEncode(testSubscription), 200));
    
    when(mockClient.get(
      any,
      headers: anyNamed('headers'),
    )).thenAnswer((_) async => http.Response(jsonEncode(testPlans), 200));
    
    // 모킹 서비스에 대한 기본 동작 정의
    when(mockPurchaseService.isLoading).thenReturn(false);
    when(mockPurchaseService.error).thenReturn(null);
    when(mockPurchaseService.products).thenReturn([]);
    when(mockPurchaseService.restorePurchases()).thenAnswer((_) async => true);
    
    // SubscriptionService 초기화 - 테스트용 생성자 사용
    subscriptionService = SubscriptionService.forTest(
      client: mockClient,
      purchaseService: mockPurchaseService,
      userId: testUserId,
      token: testToken,
      clubId: testClubId,
    );
  });

  tearDown(() async {
    // 테스트에서 생성된 SubscriptionService 정리
    try { subscriptionService.dispose(); } catch (_) {}
    
    // EventBus 표준 정리
    await EventBusTestUtils.tearDown();
    
    // 테스트 모드 해제
    TestUtils.setTestMode(false);
  });

  group('SubscriptionService 기본 기능 테스트', () {
    test('SubscriptionService 초기화 테스트', () {
      // 초기화 후 기본 상태 확인
      expect(subscriptionService, isNotNull);
      expect(subscriptionService.currentSubscription, isNull);
      expect(subscriptionService.isLoading, isNotNull);
      expect(subscriptionService.availablePlans, isNotNull);
    });
    
    test('loadCurrentSubscription 메서드가 현재 구독 정보를 가져와야 함', () async {
      // given
      when(mockClient.post(
        any,
        headers: anyNamed('headers'),
        body: anyNamed('body'),
      )).thenAnswer((_) async => http.Response(jsonEncode(testSubscription), 200));

      // when
      await subscriptionService.loadCurrentSubscription();

      // then
      expect(subscriptionService.currentSubscription, isNotNull);
      expect(subscriptionService.isLoading, isFalse);
      expect(subscriptionService.error, isNull);
    });
    
    test('canUseFeature 메서드가 기능 사용 가능 여부를 확인해야 함', () {
      // 구독이 없는 경우 기능 사용 가능 여부 확인
      expect(subscriptionService.canUseFeature('advancedStats'), isA<bool>());
    });

    test('getMaxMembers 메서드가 최대 회원 수를 반환해야 함', () {
      // 구독이 없는 경우 기본 무료 플랜의 최대 회원 수 반환
      final maxMembers = subscriptionService.getMaxMembers();
      expect(maxMembers, isNotNull);
      expect(maxMembers, isA<int>());
    });

    test('getMaxEvents 메서드가 최대 이벤트 수를 반환해야 함', () {
      // 구독이 없는 경우 기본 무료 플랜의 최대 이벤트 수 반환
      final maxEvents = subscriptionService.getMaxEvents();
      expect(maxEvents, isNotNull);
      expect(maxEvents, isA<int>());
    });
    
    test('purchaseService getter가 null이 아니어야 함', () {
      expect(subscriptionService.purchaseService, isNotNull);
    });
    
    test('hasSubscription getter가 동작해야 함', () {
      expect(subscriptionService.hasSubscription, isA<bool>());
    });
    
    test('isActive getter가 동작해야 함', () {
      expect(subscriptionService.isActive, isA<bool>());
    });
    
    test('restoreSubscriptions 메서드가 구독을 복원해야 함', () async {
      // given
      when(mockPurchaseService.restorePurchases()).thenAnswer((_) async => true);
      when(mockClient.post(
        any,
        headers: anyNamed('headers'),
        body: anyNamed('body'),
      )).thenAnswer((_) async => http.Response(jsonEncode(testSubscription), 200));
      
      // when
      final result = await subscriptionService.restoreSubscriptions();
      
      // then
      expect(result, isTrue);
      expect(subscriptionService.isLoading, isFalse);
    });
  });
}
