import 'package:get_it/get_it.dart'; import 'package:superport/core/errors/exceptions.dart'; import 'package:superport/core/errors/failures.dart'; import 'package:superport/core/utils/equipment_status_converter.dart'; import 'package:superport/data/datasources/remote/equipment_remote_datasource.dart'; import 'package:superport/data/models/common/paginated_response.dart'; import 'package:superport/data/models/equipment/equipment_history_dto.dart'; import 'package:superport/data/models/equipment/equipment_in_request.dart'; import 'package:superport/data/models/equipment/equipment_io_response.dart'; import 'package:superport/data/models/equipment/equipment_list_dto.dart'; import 'package:superport/data/models/equipment/equipment_out_request.dart'; import 'package:superport/data/models/equipment/equipment_request.dart'; import 'package:superport/data/models/equipment/equipment_response.dart'; import 'package:superport/models/equipment_unified_model.dart'; class EquipmentService { final EquipmentRemoteDataSource _remoteDataSource = GetIt.instance(); // 장비 목록 조회 (DTO 형태로 반환하여 status 정보 유지) Future> getEquipmentsWithStatus({ int page = 1, int perPage = 20, String? status, int? companyId, int? warehouseLocationId, String? search, bool includeInactive = false, }) async { try { final response = await _remoteDataSource.getEquipments( page: page, perPage: perPage, status: status, companyId: companyId, warehouseLocationId: warehouseLocationId, search: search, isActive: !includeInactive, ); return PaginatedResponse( items: response.items, page: response.page, size: response.perPage, totalElements: response.total, totalPages: response.totalPages, first: response.page == 1, last: response.page >= response.totalPages, ); } on ServerException catch (e) { throw ServerFailure(message: e.message); } catch (e) { throw ServerFailure(message: 'Failed to fetch equipment list: $e'); } } // 장비 목록 조회 Future> getEquipments({ int page = 1, int perPage = 20, String? status, int? companyId, int? warehouseLocationId, String? search, bool includeInactive = false, }) async { try { final response = await _remoteDataSource.getEquipments( page: page, perPage: perPage, status: status, companyId: companyId, warehouseLocationId: warehouseLocationId, search: search, isActive: !includeInactive, ); return PaginatedResponse( items: response.items.map((dto) => _convertListDtoToEquipment(dto)).toList(), page: response.page, size: response.perPage, totalElements: response.total, totalPages: response.totalPages, first: response.page == 1, last: response.page >= response.totalPages, ); } on ServerException catch (e) { throw ServerFailure(message: e.message); } catch (e) { throw ServerFailure(message: 'Failed to fetch equipment list: $e'); } } // 입고된 장비 목록 조회 Future> getEquipmentInList({ int page = 1, int perPage = 20, int? companyId, int? warehouseLocationId, String? search, }) async { return getEquipmentsWithStatus( page: page, perPage: perPage, status: 'available', // 입고된 장비는 사용 가능 상태 companyId: companyId, warehouseLocationId: warehouseLocationId, search: search, ); } // 출고된 장비 목록 조회 Future> getEquipmentOutList({ int page = 1, int perPage = 20, int? companyId, int? warehouseLocationId, String? search, }) async { return getEquipmentsWithStatus( page: page, perPage: perPage, status: 'in_use', // 출고된 장비는 사용 중 상태 companyId: companyId, warehouseLocationId: warehouseLocationId, search: search, ); } // 장비 생성 Future createEquipment(Equipment equipment) async { try { final request = CreateEquipmentRequest( // 🔧 [BUG FIX] 사용자가 입력한 장비 번호를 우선 사용, 없으면 자동 생성 // 기존: 항상 타임스탬프 기반 자동 생성으로 사용자 입력 무시 // 수정: equipment.equipmentNumber가 있으면 우선 사용, null/empty면 자동 생성 equipmentNumber: equipment.equipmentNumber?.isNotEmpty == true ? equipment.equipmentNumber! // 사용자 입력값 사용 : 'EQ-${DateTime.now().millisecondsSinceEpoch}', // 자동 생성 fallback category1: equipment.category1, // deprecated category 제거 category2: equipment.category2, // deprecated subCategory 제거 category3: equipment.category3, // deprecated subSubCategory 제거 manufacturer: equipment.manufacturer, modelName: equipment.modelName, // deprecated name 제거 serialNumber: equipment.serialNumber, barcode: equipment.barcode, purchaseDate: equipment.inDate, purchasePrice: equipment.purchasePrice, // 🔧 [BUG FIX] currentCompanyId → companyId 필드 수정 // 문제: Controller에서 selectedCompanyId를 equipment.companyId로 설정하는데 // EquipmentService에서 equipment.currentCompanyId를 참조해서 null 전송 // 해결: equipment.companyId 참조로 변경하여 실제 선택값 전송 companyId: equipment.companyId, warehouseLocationId: equipment.warehouseLocationId, lastInspectionDate: equipment.lastInspectionDate, nextInspectionDate: equipment.nextInspectionDate, remark: equipment.remark, ); final response = await _remoteDataSource.createEquipment(request); return _convertResponseToEquipment(response); } on ServerException catch (e) { throw ServerFailure(message: e.message); } catch (e) { throw ServerFailure(message: 'Failed to create equipment: $e'); } } // 장비 상세 조회 Future getEquipmentDetail(int id) async { print('DEBUG [EquipmentService.getEquipmentDetail] Called with ID: $id'); try { final response = await _remoteDataSource.getEquipmentDetail(id); print('DEBUG [EquipmentService.getEquipmentDetail] Response received from datasource'); print('DEBUG [EquipmentService.getEquipmentDetail] Response data: ${response.toJson()}'); final equipment = _convertResponseToEquipment(response); print('DEBUG [EquipmentService.getEquipmentDetail] Converted to Equipment model'); print('DEBUG [EquipmentService.getEquipmentDetail] Equipment.manufacturer="${equipment.manufacturer}"'); print('DEBUG [EquipmentService.getEquipmentDetail] Equipment.equipmentNumber="${equipment.equipmentNumber}"'); // deprecated name 제거 return equipment; } on ServerException catch (e) { print('ERROR [EquipmentService.getEquipmentDetail] ServerException: ${e.message}'); throw ServerFailure(message: e.message); } catch (e, stackTrace) { print('ERROR [EquipmentService.getEquipmentDetail] Unexpected error: $e'); print('ERROR [EquipmentService.getEquipmentDetail] Stack trace: $stackTrace'); throw ServerFailure(message: 'Failed to fetch equipment detail: $e'); } } // 장비 조회 (getEquipmentDetail의 alias) Future getEquipment(int id) async { return getEquipmentDetail(id); } // 장비 수정 Future updateEquipment(int id, Equipment equipment) async { try { final request = UpdateEquipmentRequest( category1: equipment.category1.isNotEmpty ? equipment.category1 : null, // deprecated category 제거 category2: equipment.category2.isNotEmpty ? equipment.category2 : null, // deprecated subCategory 제거 category3: equipment.category3.isNotEmpty ? equipment.category3 : null, // deprecated subSubCategory 제거 manufacturer: equipment.manufacturer.isNotEmpty ? equipment.manufacturer : null, modelName: equipment.modelName.isNotEmpty ? equipment.modelName : null, // deprecated name 제거 serialNumber: equipment.serialNumber?.isNotEmpty == true ? equipment.serialNumber : null, barcode: equipment.barcode?.isNotEmpty == true ? equipment.barcode : null, purchaseDate: equipment.purchaseDate, purchasePrice: equipment.purchasePrice, status: (equipment.equipmentStatus != null && equipment.equipmentStatus != 'null' && equipment.equipmentStatus!.isNotEmpty) ? EquipmentStatusConverter.clientToServer(equipment.equipmentStatus) : null, companyId: equipment.companyId, warehouseLocationId: equipment.warehouseLocationId, lastInspectionDate: equipment.lastInspectionDate, nextInspectionDate: equipment.nextInspectionDate, remark: equipment.remark?.isNotEmpty == true ? equipment.remark : null, ); // 디버그 로그 추가 - 전송되는 데이터 확인 print('DEBUG [EquipmentService.updateEquipment] Equipment model data:'); print(' equipment.equipmentStatus: "${equipment.equipmentStatus}"'); print(' equipment.equipmentStatus type: ${equipment.equipmentStatus.runtimeType}'); print(' equipment.equipmentStatus == null: ${equipment.equipmentStatus == null}'); print(' equipment.equipmentStatus == "null": ${equipment.equipmentStatus == "null"}'); String? convertedStatus; if (equipment.equipmentStatus != null) { convertedStatus = EquipmentStatusConverter.clientToServer(equipment.equipmentStatus); print(' converted status: "$convertedStatus"'); } else { print(' status is null, will not set in request'); } print('DEBUG [EquipmentService.updateEquipment] Request data:'); print(' manufacturer: "${request.manufacturer}"'); print(' modelName: "${request.modelName}"'); print(' serialNumber: "${request.serialNumber}"'); print(' status: "${request.status}"'); print(' companyId: ${request.companyId}'); print(' warehouseLocationId: ${request.warehouseLocationId}'); // JSON 직렬화 확인 final jsonData = request.toJson(); print('DEBUG [EquipmentService.updateEquipment] JSON data:'); jsonData.forEach((key, value) { print(' $key: $value (${value.runtimeType})'); }); final response = await _remoteDataSource.updateEquipment(id, request); return _convertResponseToEquipment(response); } on ServerException catch (e) { throw ServerFailure(message: e.message); } catch (e) { throw ServerFailure(message: 'Failed to update equipment: $e'); } } // 장비 삭제 Future deleteEquipment(int id) async { try { await _remoteDataSource.deleteEquipment(id); } on ServerException catch (e) { throw ServerFailure(message: e.message); } catch (e) { throw ServerFailure(message: 'Failed to delete equipment: $e'); } } // 장비 상태 변경 Future changeEquipmentStatus(int id, String status, String? reason) async { try { final response = await _remoteDataSource.changeEquipmentStatus(id, status, reason); return _convertResponseToEquipment(response); } on ServerException catch (e) { throw ServerFailure(message: e.message); } catch (e) { throw ServerFailure(message: 'Failed to change equipment status: $e'); } } // 장비 이력 추가 Future addEquipmentHistory(int equipmentId, String type, int quantity, String? remarks) async { try { final request = CreateHistoryRequest( transactionType: type, quantity: quantity, transactionDate: DateTime.now(), remarks: remarks, ); return await _remoteDataSource.addEquipmentHistory(equipmentId, request); } on ServerException catch (e) { throw ServerFailure(message: e.message); } catch (e) { throw ServerFailure(message: 'Failed to add equipment history: $e'); } } // 장비 이력 조회 Future> getEquipmentHistory(int equipmentId, {int page = 1, int perPage = 20}) async { try { return await _remoteDataSource.getEquipmentHistory(equipmentId, page: page, perPage: perPage); } on ServerException catch (e) { throw ServerFailure(message: e.message); } catch (e) { throw ServerFailure(message: 'Failed to fetch equipment history: $e'); } } // 장비 입고 Future equipmentIn({ required int equipmentId, required int quantity, int? warehouseLocationId, String? notes, }) async { try { final request = EquipmentInRequest( equipmentId: equipmentId, quantity: quantity, warehouseLocationId: warehouseLocationId, notes: notes, ); return await _remoteDataSource.equipmentIn(request); } on ServerException catch (e) { throw ServerFailure(message: e.message); } catch (e) { throw ServerFailure(message: 'Failed to process equipment in: $e'); } } // 장비 출고 Future equipmentOut({ required int equipmentId, required int quantity, required int companyId, String? notes, }) async { try { final request = EquipmentOutRequest( equipmentId: equipmentId, quantity: quantity, companyId: companyId, notes: notes, ); return await _remoteDataSource.equipmentOut(request); } on ServerException catch (e) { throw ServerFailure(message: e.message); } catch (e) { throw ServerFailure(message: 'Failed to process equipment out: $e'); } } // Private helper methods for model conversion Equipment _convertListDtoToEquipment(EquipmentListDto dto) { return Equipment( id: dto.id, manufacturer: dto.manufacturer, equipmentNumber: dto.equipmentNumber ?? '', // name → equipmentNumber (required) modelName: dto.modelName ?? '', // 새로운 필수 필드 (required) category1: '', // category → category1 (required) category2: '', // subCategory → category2 (required) category3: '', // subSubCategory → category3 (required) serialNumber: dto.serialNumber, barcode: null, // Not in list DTO quantity: 1, // Default quantity purchaseDate: dto.createdAt, // purchaseDate로 변경 inDate: dto.createdAt, // 기존 inDate 유지 remark: null, // Not in list DTO // 백엔드 API 새로운 필드들 (리스트 DTO에서는 제한적) currentCompanyId: dto.companyId, warehouseLocationId: dto.warehouseLocationId, equipmentStatus: dto.status, ); } Equipment _convertResponseToEquipment(EquipmentResponse response) { return Equipment( id: response.id, manufacturer: response.manufacturer, equipmentNumber: response.equipmentNumber ?? '', // name → equipmentNumber (required) modelName: response.modelName ?? '', // 새로운 필수 필드 (required) category1: response.category1 ?? '', // category → category1 (required) category2: response.category2 ?? '', // subCategory → category2 (required) category3: response.category3 ?? '', // subSubCategory → category3 (required) serialNumber: response.serialNumber, barcode: response.barcode, quantity: 1, // Default quantity, actual quantity should be tracked in history purchaseDate: response.purchaseDate, // purchaseDate로 변경 inDate: response.purchaseDate, // 기존 inDate 유지 remark: response.remark, // 백엔드 API 새로운 필드들 매핑 - 백엔드 완전 호환 purchasePrice: response.purchasePrice != null ? double.tryParse(response.purchasePrice!) : null, currentCompanyId: response.companyId, warehouseLocationId: response.warehouseLocationId, companyId: response.companyId, lastInspectionDate: response.lastInspectionDate, nextInspectionDate: response.nextInspectionDate, equipmentStatus: response.status, // 중복 필드 제거 완료 - 대부분의 필드는 이미 위에서 정의됨 ); } // 장비 상태 상수 static const Map equipmentStatus = { 'available': '사용 가능', 'in_use': '사용 중', 'maintenance': '유지보수 중', 'repair': '수리 중', 'disposed': '폐기', }; }