// ignore_for_file: avoid_print

import 'package:get_it/get_it.dart';
import 'package:flutter/foundation.dart';
import 'package:dio/dio.dart';
import 'package:superport/data/datasources/remote/api_client.dart';
import 'package:superport/services/equipment_service.dart';
import '../../framework/core/auto_test_system.dart';
import '../../framework/core/api_error_diagnostics.dart';
import '../../framework/core/auto_fixer.dart';
import '../../framework/core/test_data_generator.dart';
import '../../framework/infrastructure/report_collector.dart';
import '../../../real_api/test_helper.dart';

/// 커스텀 assertion 헬퍼 함수들
void assertEqual(dynamic actual, dynamic expected, {String? message}) {
  if (actual != expected) {
    throw AssertionError(
      message ?? 'Expected $expected but got $actual'
    );
  }
}

void assertNotNull(dynamic value, {String? message}) {
  if (value == null) {
    throw AssertionError(message ?? 'Expected non-null value but got null');
  }
}

void assertTrue(bool condition, {String? message}) {
  if (!condition) {
    throw AssertionError(message ?? 'Expected true but got false');
  }
}

void assertIsNotEmpty(dynamic collection, {String? message}) {
  if (collection == null || (collection is Iterable && collection.isEmpty) || 
      (collection is Map && collection.isEmpty)) {
    throw AssertionError(message ?? 'Expected non-empty collection');
  }
}

/// 장비 입고 화면 전체 기능 자동 테스트
/// 
/// 테스트 항목:
/// 1. 장비 목록 조회
/// 2. 장비 검색 및 필터링
/// 3. 새 장비 등록
/// 4. 장비 정보 수정
/// 5. 장비 삭제
/// 6. 장비 상태 변경
/// 7. 장비 이력 추가
/// 8. 이미지 업로드
/// 9. 바코드 스캔 시뮬레이션
/// 10. 입고 완료 처리
class EquipmentInFullTest {
  late AutoTestSystem autoTestSystem;
  late EquipmentService equipmentService;
  late ApiClient apiClient;
  late GetIt getIt;
  
  // 테스트 중 생성된 리소스 추적
  final List<int> createdEquipmentIds = [];
  
  Future<void> setup() async {
    debugPrint('\n[EquipmentInFullTest] 테스트 환경 설정 중...');
    
    // 환경 초기화
    await RealApiTestHelper.setupTestEnvironment();
    getIt = GetIt.instance;
    apiClient = getIt.get<ApiClient>();
    
    // 자동 테스트 시스템 초기화
    autoTestSystem = AutoTestSystem(
      apiClient: apiClient,
      getIt: getIt,
      errorDiagnostics: ApiErrorDiagnostics(),
      autoFixer: ApiAutoFixer(diagnostics: ApiErrorDiagnostics()),
      dataGenerator: TestDataGenerator(),
      reportCollector: ReportCollector(),
    );
    
    // 서비스 초기화
    equipmentService = getIt.get<EquipmentService>();
    
    // 인증
    await autoTestSystem.ensureAuthenticated();
    
    debugPrint('[EquipmentInFullTest] 설정 완료\n');
  }
  
  Future<void> teardown() async {
    debugPrint('\n[EquipmentInFullTest] 테스트 정리 중...');
    
    // 생성된 장비 삭제
    for (final id in createdEquipmentIds) {
      try {
        await equipmentService.deleteEquipment(id);
        debugPrint('[EquipmentInFullTest] 장비 삭제: ID $id');
      } catch (e) {
        debugPrint('[EquipmentInFullTest] 장비 삭제 실패 (ID: $id): $e');
      }
    }
    
    await RealApiTestHelper.teardownTestEnvironment();
    debugPrint('[EquipmentInFullTest] 정리 완료\n');
  }
  
  Future<Map<String, dynamic>> runAllTests() async {
    final results = <String, dynamic>{
      'totalTests': 0,
      'passedTests': 0,
      'failedTests': 0,
      'tests': [],
    };
    
    try {
      await setup();
      
      // 테스트 목록
      final tests = [
        _test1EquipmentList,
        _test2SearchAndFilter,
        _test3CreateEquipment,
        _test4UpdateEquipment,
        _test5DeleteEquipment,
        _test6ChangeStatus,
        _test7AddHistory,
        _test8ImageUpload,
        _test9BarcodeSimulation,
        _test10CompleteIncoming,
      ];
      
      results['totalTests'] = tests.length;
      
      // 각 테스트 실행
      for (final test in tests) {
        final result = await test();
        results['tests'].add(result);
        
        if (result['passed'] == true) {
          results['passedTests']++;
        } else {
          results['failedTests']++;
        }
      }
      
    } catch (e) {
      debugPrint('[EquipmentInFullTest] 치명적 오류: $e');
    } finally {
      await teardown();
    }
    
    return results;
  }
  
  /// 테스트 1: 장비 목록 조회
  Future<Map<String, dynamic>> _test1EquipmentList() async {
    return await autoTestSystem.runTestWithAutoFix(
      testName: '장비 목록 조회',
      screenName: 'EquipmentIn',
      testFunction: () async {
        debugPrint('[TEST 1] 장비 목록 조회 시작...');
        
        // 페이지네이션 파라미터
        const page = 1;
        const perPage = 20;
        
        // API 호출
        final response = await apiClient.dio.get(
          '/equipment',
          queryParameters: {
            'page': page,
            'per_page': perPage,
          },
        );
        
        // 응답 검증
        assertEqual(response.statusCode, 200, message: '응답 상태 코드가 200이어야 합니다');
        assertNotNull(response.data, message: '응답 데이터가 null이면 안됩니다');
        assertEqual(response.data['success'], true, message: '성공 플래그가 true여야 합니다');
        assertTrue(response.data['data'] is List, message: '데이터가 리스트여야 합니다');
        
        final equipmentList = response.data['data'] as List;
        debugPrint('[TEST 1] 조회된 장비 수: ${equipmentList.length}');
        
        // 페이지네이션 정보 검증
        if (response.data['pagination'] != null) {
          final pagination = response.data['pagination'];
          assertEqual(pagination['page'], page, message: '페이지 번호가 일치해야 합니다');
          assertEqual(pagination['per_page'], perPage, message: '페이지당 항목 수가 일치해야 합니다');
          debugPrint('[TEST 1] 전체 장비 수: ${pagination['total']}');
        } else if (response.data['meta'] != null) {
          // 구버전 meta 필드 지원
          final meta = response.data['meta'];
          assertEqual(meta['page'], page, message: '페이지 번호가 일치해야 합니다');
          assertEqual(meta['per_page'], perPage, message: '페이지당 항목 수가 일치해야 합니다');
          debugPrint('[TEST 1] 전체 장비 수: ${meta['total']}');
        }
        
        // 장비 데이터 구조 검증
        if (equipmentList.isNotEmpty) {
          final firstEquipment = equipmentList.first;
          assertNotNull(firstEquipment['id'], message: '장비 ID가 있어야 합니다');
          assertNotNull(firstEquipment['equipment_number'], message: '장비 번호가 있어야 합니다');
          assertNotNull(firstEquipment['serial_number'], message: '시리얼 번호가 있어야 합니다');
          assertNotNull(firstEquipment['manufacturer'], message: '제조사가 있어야 합니다');
          assertNotNull(firstEquipment['model_name'], message: '모델명이 있어야 합니다');
          assertNotNull(firstEquipment['status'], message: '상태가 있어야 합니다');
        }
        
        debugPrint('[TEST 1] ✅ 장비 목록 조회 성공');
      },
    ).then((result) => result.toMap());
  }
  
  /// 테스트 2: 장비 검색 및 필터링
  Future<Map<String, dynamic>> _test2SearchAndFilter() async {
    return await autoTestSystem.runTestWithAutoFix(
      testName: '장비 검색 및 필터링',
      screenName: 'EquipmentIn',
      testFunction: () async {
        debugPrint('[TEST 2] 장비 검색 및 필터링 시작...');
        
        // 상태별 필터링
        final statusFilter = await apiClient.dio.get(
          '/equipment',
          queryParameters: {
            'status': 'available',
            'page': 1,
            'per_page': 10,
          },
        );
        
        assertEqual(statusFilter.statusCode, 200, message: '상태 필터링 응답이 200이어야 합니다');
        final availableEquipment = statusFilter.data['data'] as List;
        debugPrint('[TEST 2] 사용 가능한 장비 수: ${availableEquipment.length}');
        
        // 모든 조회된 장비가 'available' 상태인지 확인
        for (final equipment in availableEquipment) {
          assertEqual(equipment['status'], 'available', 
            message: '필터링된 장비의 상태가 available이어야 합니다');
        }
        
        // 회사별 필터링 (예시)
        if (availableEquipment.isNotEmpty) {
          final companyId = availableEquipment.first['company_id'];
          final companyFilter = await apiClient.dio.get(
            '/equipment',
            queryParameters: {
              'company_id': companyId,
              'page': 1,
              'per_page': 10,
            },
          );
          
          assertEqual(companyFilter.statusCode, 200, 
            message: '회사별 필터링 응답이 200이어야 합니다');
          debugPrint('[TEST 2] 회사 ID $companyId의 장비 수: ${companyFilter.data['data'].length}');
        }
        
        debugPrint('[TEST 2] ✅ 장비 검색 및 필터링 성공');
      },
    ).then((result) => result.toMap());
  }
  
  /// 테스트 3: 새 장비 등록
  Future<Map<String, dynamic>> _test3CreateEquipment() async {
    return await autoTestSystem.runTestWithAutoFix(
      testName: '새 장비 등록',
      screenName: 'EquipmentIn',
      testFunction: () async {
        debugPrint('[TEST 3] 새 장비 등록 시작...');
        
        // 테스트 데이터 생성
        final equipmentData = await autoTestSystem.generateTestData('equipment');
        debugPrint('[TEST 3] 생성할 장비 데이터: $equipmentData');
        
        // 장비 생성 API 호출
        final response = await apiClient.dio.post(
          '/equipment',
          data: equipmentData,
        );
        
        // 응답 검증 (API가 200을 반환하는 경우도 허용)
        assertTrue(response.statusCode == 200 || response.statusCode == 201, 
          message: '생성 응답 코드가 200 또는 201이어야 합니다');
        assertEqual(response.data['success'], true, message: '성공 플래그가 true여야 합니다');
        assertNotNull(response.data['data'], message: '생성된 장비 데이터가 있어야 합니다');
        
        final createdEquipment = response.data['data'];
        assertNotNull(createdEquipment['id'], message: '생성된 장비 ID가 있어야 합니다');
        assertEqual(createdEquipment['serial_number'], equipmentData['serial_number'], 
          message: '시리얼 번호가 일치해야 합니다');
        assertEqual(createdEquipment['model_name'], equipmentData['model_name'], 
          message: '모델명이 일치해야 합니다');
        
        // 생성된 장비 ID 저장 (정리용)
        createdEquipmentIds.add(createdEquipment['id']);
        
        debugPrint('[TEST 3] ✅ 장비 생성 성공 - ID: ${createdEquipment['id']}');
      },
    ).then((result) => result.toMap());
  }
  
  /// 테스트 4: 장비 정보 수정
  Future<Map<String, dynamic>> _test4UpdateEquipment() async {
    return await autoTestSystem.runTestWithAutoFix(
      testName: '장비 정보 수정',
      screenName: 'EquipmentIn',
      testFunction: () async {
        debugPrint('[TEST 4] 장비 정보 수정 시작...');
        
        // 수정할 장비가 없으면 먼저 생성
        if (createdEquipmentIds.isEmpty) {
          await _createTestEquipment();
        }
        
        final equipmentId = createdEquipmentIds.last;
        debugPrint('[TEST 4] 수정할 장비 ID: $equipmentId');
        
        // 수정 데이터
        final updateData = {
          'model_name': 'Updated Model ${DateTime.now().millisecondsSinceEpoch}',
          'status': 'maintenance',
          'notes': '정기 점검 중',
        };
        
        // 장비 수정 API 호출
        final response = await apiClient.dio.put(
          '/equipment/$equipmentId',
          data: updateData,
        );
        
        // 응답 검증
        assertEqual(response.statusCode, 200, message: '수정 응답 코드가 200이어야 합니다');
        assertEqual(response.data['success'], true, message: '성공 플래그가 true여야 합니다');
        
        final updatedEquipment = response.data['data'];
        assertEqual(updatedEquipment['model_name'], updateData['model_name'], 
          message: '수정된 모델명이 일치해야 합니다');
        assertEqual(updatedEquipment['status'], updateData['status'], 
          message: '수정된 상태가 일치해야 합니다');
        
        debugPrint('[TEST 4] ✅ 장비 정보 수정 성공');
      },
    ).then((result) => result.toMap());
  }
  
  /// 테스트 5: 장비 삭제
  Future<Map<String, dynamic>> _test5DeleteEquipment() async {
    return await autoTestSystem.runTestWithAutoFix(
      testName: '장비 삭제',
      screenName: 'EquipmentIn',
      testFunction: () async {
        debugPrint('[TEST 5] 장비 삭제 시작...');
        
        // 삭제용 장비 생성
        await _createTestEquipment();
        final equipmentId = createdEquipmentIds.last;
        debugPrint('[TEST 5] 삭제할 장비 ID: $equipmentId');
        
        // 장비 삭제 API 호출
        final response = await apiClient.dio.delete('/equipment/$equipmentId');
        
        // 응답 검증
        assertEqual(response.statusCode, 200, message: '삭제 응답 코드가 200이어야 합니다');
        assertEqual(response.data['success'], true, message: '성공 플래그가 true여야 합니다');
        
        // 삭제된 장비 조회 시도 (404 예상)
        try {
          await apiClient.dio.get('/equipment/$equipmentId');
          throw AssertionError('삭제된 장비가 여전히 조회됨');
        } on DioException catch (e) {
          assertEqual(e.response?.statusCode, 404, 
            message: '삭제된 장비 조회 시 404를 반환해야 합니다');
        }
        
        // 정리 목록에서 제거
        createdEquipmentIds.remove(equipmentId);
        
        debugPrint('[TEST 5] ✅ 장비 삭제 성공');
      },
    ).then((result) => result.toMap());
  }
  
  /// 테스트 6: 장비 상태 변경
  Future<Map<String, dynamic>> _test6ChangeStatus() async {
    return await autoTestSystem.runTestWithAutoFix(
      testName: '장비 상태 변경',
      screenName: 'EquipmentIn',
      testFunction: () async {
        debugPrint('[TEST 6] 장비 상태 변경 시작...');
        
        // 상태 변경할 장비가 없으면 생성
        if (createdEquipmentIds.isEmpty) {
          await _createTestEquipment();
        }
        
        final equipmentId = createdEquipmentIds.last;
        debugPrint('[TEST 6] 상태 변경할 장비 ID: $equipmentId');
        
        // 상태 변경 데이터
        final statusData = {
          'status': 'in_use',
          'reason': '창고 A에서 사용 중',
        };
        
        // 상태 변경 API 호출
        final response = await apiClient.dio.patch(
          '/equipment/$equipmentId/status',
          data: statusData,
        );
        
        // 응답 검증
        assertEqual(response.statusCode, 200, message: '상태 변경 응답 코드가 200이어야 합니다');
        assertEqual(response.data['success'], true, message: '성공 플래그가 true여야 합니다');
        
        final updatedEquipment = response.data['data'];
        assertEqual(updatedEquipment['status'], statusData['status'], 
          message: '변경된 상태가 일치해야 합니다');
        
        debugPrint('[TEST 6] ✅ 장비 상태 변경 성공');
      },
    ).then((result) => result.toMap());
  }
  
  /// 테스트 7: 장비 이력 추가
  Future<Map<String, dynamic>> _test7AddHistory() async {
    return await autoTestSystem.runTestWithAutoFix(
      testName: '장비 이력 추가',
      screenName: 'EquipmentIn',
      testFunction: () async {
        debugPrint('[TEST 7] 장비 이력 추가 시작...');
        
        // 이력 추가할 장비가 없으면 생성
        if (createdEquipmentIds.isEmpty) {
          await _createTestEquipment();
        }
        
        final equipmentId = createdEquipmentIds.last;
        debugPrint('[TEST 7] 이력 추가할 장비 ID: $equipmentId');
        
        // 이력 데이터
        final historyData = {
          'transaction_type': 'maintenance',
          'transaction_date': DateTime.now().toIso8601String().split('T')[0],
          'description': '정기 점검 완료',
          'performed_by': 'Test User',
          'cost': 50000,
          'notes': '다음 점검일: ${DateTime.now().add(Duration(days: 90)).toIso8601String().split('T')[0]}',
        };
        
        // 이력 추가 API 호출
        final response = await apiClient.dio.post(
          '/equipment/$equipmentId/history',
          data: historyData,
        );
        
        // 응답 검증
        assertEqual(response.statusCode, 201, message: '이력 추가 응답 코드가 201이어야 합니다');
        assertEqual(response.data['success'], true, message: '성공 플래그가 true여야 합니다');
        
        final createdHistory = response.data['data'];
        assertNotNull(createdHistory['id'], message: '생성된 이력 ID가 있어야 합니다');
        assertEqual(createdHistory['equipment_id'], equipmentId, 
          message: '이력의 장비 ID가 일치해야 합니다');
        assertEqual(createdHistory['transaction_type'], historyData['transaction_type'], 
          message: '거래 유형이 일치해야 합니다');
        
        debugPrint('[TEST 7] ✅ 장비 이력 추가 성공 - 이력 ID: ${createdHistory['id']}');
      },
    ).then((result) => result.toMap());
  }
  
  /// 테스트 8: 이미지 업로드 (시뮬레이션)
  Future<Map<String, dynamic>> _test8ImageUpload() async {
    return await autoTestSystem.runTestWithAutoFix(
      testName: '이미지 업로드',
      screenName: 'EquipmentIn',
      testFunction: () async {
        debugPrint('[TEST 8] 이미지 업로드 시뮬레이션...');
        
        // 실제 이미지 업로드는 파일 시스템 접근이 필요하므로
        // 여기서는 메타데이터만 테스트
        
        if (createdEquipmentIds.isEmpty) {
          await _createTestEquipment();
        }
        
        final equipmentId = createdEquipmentIds.last;
        debugPrint('[TEST 8] 이미지 업로드할 장비 ID: $equipmentId');
        
        // 이미지 메타데이터 (실제로는 multipart/form-data로 전송)
        // 실제 구현에서는 다음과 같은 메타데이터가 포함됨:
        // - 'caption': '장비 전면 사진'
        // - 'taken_date': DateTime.now().toIso8601String()
        
        debugPrint('[TEST 8] 이미지 업로드 시뮬레이션 완료');
        debugPrint('[TEST 8] ✅ 테스트 통과 (시뮬레이션)');
      },
    ).then((result) => result.toMap());
  }
  
  /// 테스트 9: 바코드 스캔 시뮬레이션
  Future<Map<String, dynamic>> _test9BarcodeSimulation() async {
    return await autoTestSystem.runTestWithAutoFix(
      testName: '바코드 스캔 시뮬레이션',
      screenName: 'EquipmentIn',
      testFunction: () async {
        debugPrint('[TEST 9] 바코드 스캔 시뮬레이션...');
        
        // 바코드 스캔 결과 시뮬레이션
        final simulatedBarcode = 'EQ-${DateTime.now().millisecondsSinceEpoch}';
        debugPrint('[TEST 9] 시뮬레이션 바코드: $simulatedBarcode');
        
        // 바코드로 장비 검색 시뮬레이션
        try {
          final response = await apiClient.dio.get(
            '/equipment',
            queryParameters: {
              'serial_number': simulatedBarcode,
            },
          );
          
          final results = response.data['data'] as List;
          if (results.isEmpty) {
            debugPrint('[TEST 9] 바코드에 해당하는 장비 없음 - 새 장비 등록 필요');
          } else {
            debugPrint('[TEST 9] 바코드에 해당하는 장비 찾음: ${results.first['name']}');
          }
        } catch (e) {
          debugPrint('[TEST 9] 바코드 검색 중 에러 (예상됨): $e');
        }
        
        debugPrint('[TEST 9] ✅ 바코드 스캔 시뮬레이션 완료');
      },
    ).then((result) => result.toMap());
  }
  
  /// 테스트 10: 입고 완료 처리
  Future<Map<String, dynamic>> _test10CompleteIncoming() async {
    return await autoTestSystem.runTestWithAutoFix(
      testName: '입고 완료 처리',
      screenName: 'EquipmentIn',
      testFunction: () async {
        debugPrint('[TEST 10] 입고 완료 처리 시작...');
        
        // 입고 처리할 장비가 없으면 생성
        if (createdEquipmentIds.isEmpty) {
          await _createTestEquipment();
        }
        
        final equipmentId = createdEquipmentIds.last;
        debugPrint('[TEST 10] 입고 처리할 장비 ID: $equipmentId');
        
        // 입고 완료 이력 추가
        final incomingData = {
          'transaction_type': 'check_in',
          'transaction_date': DateTime.now().toIso8601String().split('T')[0],
          'description': '신규 장비 입고 완료',
          'performed_by': 'Warehouse Manager',
          'notes': '양호한 상태로 입고됨',
        };
        
        // 이력 추가 API 호출
        final historyResponse = await apiClient.dio.post(
          '/equipment/$equipmentId/history',
          data: incomingData,
        );
        
        assertEqual(historyResponse.statusCode, 201, 
          message: '입고 이력 추가 응답 코드가 201이어야 합니다');
        
        // 상태를 'available'로 변경
        final statusResponse = await apiClient.dio.patch(
          '/equipment/$equipmentId/status',
          data: {
            'status': 'available',
            'reason': '입고 완료 - 사용 가능',
          },
        );
        
        assertEqual(statusResponse.statusCode, 200, 
          message: '상태 변경 응답 코드가 200이어야 합니다');
        assertEqual(statusResponse.data['data']['status'], 'available', 
          message: '입고 완료 후 상태가 available이어야 합니다');
        
        debugPrint('[TEST 10] ✅ 입고 완료 처리 성공');
      },
    ).then((result) => result.toMap());
  }
  
  /// 테스트용 장비 생성 헬퍼
  Future<void> _createTestEquipment() async {
    try {
      final equipmentData = await autoTestSystem.generateTestData('equipment');
      final response = await apiClient.dio.post('/equipment', data: equipmentData);
      
      if ((response.statusCode == 200 || response.statusCode == 201) && 
          response.data['success'] == true) {
        final createdEquipment = response.data['data'];
        if (createdEquipment != null && createdEquipment['id'] != null) {
          createdEquipmentIds.add(createdEquipment['id']);
          debugPrint('[Helper] 테스트 장비 생성 완료 - ID: ${createdEquipment['id']}');
        }
      }
    } catch (e) {
      debugPrint('[Helper] 테스트 장비 생성 실패: $e');
      rethrow;
    }
  }
}

// Extension to convert TestResult to Map
extension TestResultExtension on TestResult {
  Map<String, dynamic> toMap() {
    return {
      'testName': testName,
      'passed': passed,
      'error': error,
      'retryCount': retryCount,
    };
  }
}