import 'package:flutter/material.dart'; import 'package:injectable/injectable.dart'; import '../../../data/models/equipment/equipment_dto.dart'; import '../../../data/models/company/company_dto.dart'; import '../../../data/models/model/model_dto.dart'; import '../../../data/models/vendor_dto.dart'; import '../../../domain/usecases/equipment/create_equipment_usecase.dart'; import '../../../domain/usecases/equipment/update_equipment_usecase.dart'; import '../../../domain/usecases/equipment/get_equipment_detail_usecase.dart'; import '../../../domain/usecases/company/get_companies_usecase.dart'; import '../../../domain/usecases/models/get_models_usecase.dart'; import '../../../domain/usecases/vendor_usecase.dart'; import '../../../core/errors/failures.dart'; /// 장비 폼 컨트롤러 (생성/수정) /// Phase 8 - Clean Architecture 패턴, 복합 FK 지원 @injectable class EquipmentFormController extends ChangeNotifier { final CreateEquipmentUseCase _createEquipmentUseCase; final UpdateEquipmentUseCase _updateEquipmentUseCase; final GetEquipmentDetailUseCase _getEquipmentDetailUseCase; final GetCompaniesUseCase _getCompaniesUseCase; final GetModelsUseCase _getModelsUseCase; final VendorUseCase _vendorUseCase; EquipmentFormController( this._createEquipmentUseCase, this._updateEquipmentUseCase, this._getEquipmentDetailUseCase, this._getCompaniesUseCase, this._getModelsUseCase, this._vendorUseCase, ); // 상태 관리 bool _isLoading = false; bool _isLoadingCompanies = false; bool _isLoadingVendors = false; bool _isLoadingModels = false; bool _isSaving = false; String? _error; // 폼 데이터 EquipmentDto? _currentEquipment; int? _equipmentId; // 수정 모드일 때 사용 // 드롭다운 데이터 List _companies = []; List _vendors = []; List _models = []; List _filteredModels = []; // 선택된 값 int? _selectedCompanyId; int? _selectedVendorId; int? _selectedModelId; // 폼 컨트롤러들 final TextEditingController serialNumberController = TextEditingController(); final TextEditingController barcodeController = TextEditingController(); final TextEditingController purchasePriceController = TextEditingController(); final TextEditingController warrantyNumberController = TextEditingController(); final TextEditingController remarkController = TextEditingController(); // 날짜 필드들 DateTime? _purchasedAt; DateTime _warrantyStartedAt = DateTime.now().toUtc(); DateTime _warrantyEndedAt = DateTime.now().toUtc().add(const Duration(days: 365)); // Getters bool get isLoading => _isLoading; bool get isLoadingCompanies => _isLoadingCompanies; bool get isLoadingVendors => _isLoadingVendors; bool get isLoadingModels => _isLoadingModels; bool get isSaving => _isSaving; String? get error => _error; EquipmentDto? get currentEquipment => _currentEquipment; bool get isEditMode => _equipmentId != null; List get companies => _companies; List get vendors => _vendors; List get filteredModels => _filteredModels; int? get selectedCompanyId => _selectedCompanyId; int? get selectedVendorId => _selectedVendorId; int? get selectedModelId => _selectedModelId; DateTime? get purchasedAt => _purchasedAt; DateTime get warrantyStartedAt => _warrantyStartedAt; DateTime get warrantyEndedAt => _warrantyEndedAt; /// 초기화 (생성 모드) Future initializeForCreate() async { _equipmentId = null; _currentEquipment = null; _clearForm(); await _loadInitialData(); } /// 초기화 (수정 모드) Future initializeForEdit(int equipmentId) async { _equipmentId = equipmentId; _isLoading = true; _error = null; notifyListeners(); try { await _loadInitialData(); await _loadEquipmentDetail(equipmentId); } catch (e) { _error = '장비 정보를 불러오는데 실패했습니다: $e'; } finally { _isLoading = false; notifyListeners(); } } /// 초기 데이터 로드 (회사, 제조사, 모델) Future _loadInitialData() async { await Future.wait([ _loadCompanies(), _loadVendors(), _loadModels(), ]); } /// 회사 목록 로드 Future _loadCompanies() async { _isLoadingCompanies = true; notifyListeners(); try { final params = GetCompaniesParams(page: 1, perPage: 1000); // 모든 회사 가져오기 final result = await _getCompaniesUseCase(params); result.fold( (failure) { _error = _getErrorMessage(failure); }, (paginatedResponse) { _companies = paginatedResponse.items .cast() // 타입 캐스팅 추가 .where((company) => company.isActive) .toList() ..sort((a, b) => a.name.compareTo(b.name)); }, ); } catch (e) { _error = '회사 목록을 불러오는데 실패했습니다: $e'; } finally { _isLoadingCompanies = false; notifyListeners(); } } /// 제조사 목록 로드 Future _loadVendors() async { _isLoadingVendors = true; notifyListeners(); try { final vendorResponse = await _vendorUseCase.getVendors(limit: 1000); // 모든 제조사 가져오기 _vendors = (vendorResponse.items as List) .whereType() .where((vendor) => vendor.isActive) .toList() ..sort((a, b) => a.name.compareTo(b.name)); } catch (e) { _error = '제조사 목록을 불러오는데 실패했습니다: $e'; } finally { _isLoadingVendors = false; notifyListeners(); } } /// 모델 목록 로드 Future _loadModels() async { _isLoadingModels = true; notifyListeners(); try { const params = GetModelsParams(page: 1, perPage: 1000); final result = await _getModelsUseCase(params); result.fold( (failure) => throw Exception(failure.message), (modelResponse) { _models = modelResponse.items; _filteredModels = _models; }, ); } catch (e) { _error = '모델 목록을 불러오는데 실패했습니다: $e'; } finally { _isLoadingModels = false; notifyListeners(); } } /// 장비 상세 정보 로드 (수정 모드) Future _loadEquipmentDetail(int equipmentId) async { final result = await _getEquipmentDetailUseCase(equipmentId); result.fold( (failure) { _error = _getErrorMessage(failure); }, (equipment) { _currentEquipment = equipment; _populateForm(equipment); }, ); } /// 폼 필드에 데이터 채우기 (수정 모드) void _populateForm(EquipmentDto equipment) { _selectedCompanyId = equipment.companiesId; _selectedModelId = equipment.modelsId; serialNumberController.text = equipment.serialNumber; barcodeController.text = equipment.barcode ?? ''; purchasePriceController.text = equipment.purchasePrice.toString(); warrantyNumberController.text = equipment.warrantyNumber; remarkController.text = equipment.remark ?? ''; _purchasedAt = equipment.purchasedAt?.toUtc(); // ✅ UTC 타임존으로 변환 _warrantyStartedAt = equipment.warrantyStartedAt.toUtc(); // ✅ UTC 타임존으로 변환 _warrantyEndedAt = equipment.warrantyEndedAt.toUtc(); // ✅ UTC 타임존으로 변환 // 선택된 회사에 따라 모델 필터링 _filterModelsByCompany(_selectedCompanyId); notifyListeners(); } /// 회사 선택 void selectCompany(int? companyId) { _selectedCompanyId = companyId; notifyListeners(); } /// 제조사 선택 (제조사별 모델 필터링 활성화) void selectVendor(int? vendorId) { _selectedVendorId = vendorId; _selectedModelId = null; // 모델 선택 초기화 _filterModelsByVendor(vendorId); // 실제 제조사별 필터링 실행 notifyListeners(); } /// 모델 선택 void selectModel(int? modelId) { _selectedModelId = modelId; notifyListeners(); } /// 제조사별 모델 필터링 (실제 구현) void _filterModelsByVendor(int? vendorId) { if (vendorId == null) { _filteredModels = _models; } else { // vendorsId 기준으로 실제 필터링 구현 _filteredModels = _models.where((model) => model.vendorsId == vendorId).toList(); } notifyListeners(); } /// 레거시 호환성을 위한 Company 기반 필터링 (현재는 전체 모델 표시) void _filterModelsByCompany(int? companyId) { if (companyId == null) { _filteredModels = _models; } else { // 회사별 모델 필터링은 현재 구조에서는 불가능 (모든 모델 표시) _filteredModels = _models; } notifyListeners(); } /// 구매일 선택 void setPurchasedAt(DateTime? date) { _purchasedAt = date?.toUtc(); // ✅ UTC 타임존으로 변환 notifyListeners(); } /// 워런티 시작일 선택 void setWarrantyStartedAt(DateTime date) { _warrantyStartedAt = date.toUtc(); // ✅ UTC 타임존으로 변환 // 시작일이 종료일보다 늦으면 종료일을 1년 후로 설정 if (_warrantyStartedAt.isAfter(_warrantyEndedAt)) { _warrantyEndedAt = _warrantyStartedAt.add(const Duration(days: 365)); } notifyListeners(); } /// 워런티 종료일 선택 void setWarrantyEndedAt(DateTime date) { _warrantyEndedAt = date.toUtc(); // ✅ UTC 타임존으로 변환 notifyListeners(); } /// 폼 유효성 검증 String? validateForm() { if (_selectedCompanyId == null) { return '회사를 선택해주세요.'; } if (_selectedModelId == null) { return '모델을 선택해주세요.'; } if (serialNumberController.text.trim().isEmpty) { return '시리얼 번호를 입력해주세요.'; } if (warrantyNumberController.text.trim().isEmpty) { return '워런티 번호를 입력해주세요.'; } if (_warrantyStartedAt.isAfter(_warrantyEndedAt)) { return '워런티 시작일이 종료일보다 늦을 수 없습니다.'; } return null; } /// 장비 저장 (생성 또는 수정) Future saveEquipment() async { final validationError = validateForm(); if (validationError != null) { _error = validationError; notifyListeners(); return false; } _isSaving = true; _error = null; notifyListeners(); try { if (isEditMode) { return await _updateEquipment(); } else { return await _createEquipment(); } } catch (e) { _error = '저장 중 오류가 발생했습니다: $e'; return false; } finally { _isSaving = false; notifyListeners(); } } /// 장비 생성 Future _createEquipment() async { final request = EquipmentRequestDto( companiesId: _selectedCompanyId, // 백엔드: Option - null 허용 modelsId: _selectedModelId, // 백엔드: Option - null 허용 serialNumber: serialNumberController.text.trim(), barcode: barcodeController.text.trim().isNotEmpty ? barcodeController.text.trim() : null, purchasedAt: (_purchasedAt ?? DateTime.now()).toUtc(), // 백엔드: 필수 필드 - 기본값 제공 purchasePrice: int.tryParse(purchasePriceController.text) ?? 0, warrantyNumber: warrantyNumberController.text.trim(), warrantyStartedAt: _warrantyStartedAt.toUtc(), // ✅ UTC 타임존으로 변환 warrantyEndedAt: _warrantyEndedAt.toUtc(), // ✅ UTC 타임존으로 변환 remark: remarkController.text.trim().isNotEmpty ? remarkController.text.trim() : null, ); final result = await _createEquipmentUseCase(request); return result.fold( (failure) { _error = _getErrorMessage(failure); return false; }, (equipment) { _currentEquipment = equipment; return true; }, ); } /// 장비 수정 Future _updateEquipment() async { final request = EquipmentUpdateRequestDto( companiesId: _selectedCompanyId!, modelsId: _selectedModelId!, serialNumber: serialNumberController.text.trim(), barcode: barcodeController.text.trim().isNotEmpty ? barcodeController.text.trim() : null, purchasedAt: _purchasedAt?.toUtc(), // ✅ UTC 타임존으로 변환 purchasePrice: int.tryParse(purchasePriceController.text) ?? 0, warrantyNumber: warrantyNumberController.text.trim(), warrantyStartedAt: _warrantyStartedAt.toUtc(), // ✅ UTC 타임존으로 변환 warrantyEndedAt: _warrantyEndedAt.toUtc(), // ✅ UTC 타임존으로 변환 remark: remarkController.text.trim().isNotEmpty ? remarkController.text.trim() : null, ); final result = await _updateEquipmentUseCase(UpdateEquipmentParams( id: _equipmentId!, request: request, )); return result.fold( (failure) { _error = _getErrorMessage(failure); return false; }, (equipment) { _currentEquipment = equipment; return true; }, ); } /// 폼 초기화 void _clearForm() { _selectedCompanyId = null; _selectedModelId = null; serialNumberController.clear(); barcodeController.clear(); purchasePriceController.text = '0'; warrantyNumberController.clear(); remarkController.clear(); _purchasedAt = null; _warrantyStartedAt = DateTime.now().toUtc(); // ✅ UTC 타임존으로 변환 _warrantyEndedAt = DateTime.now().toUtc().add(const Duration(days: 365)); // ✅ UTC 타임존으로 변환 _error = null; } /// 에러 메시지 변환 String _getErrorMessage(Failure failure) { switch (failure.runtimeType) { case ServerFailure: return (failure as ServerFailure).message; case NetworkFailure: return '네트워크 연결을 확인해주세요.'; case ValidationFailure: return (failure as ValidationFailure).message; default: return '알 수 없는 오류가 발생했습니다.'; } } /// 에러 초기화 void clearError() { _error = null; notifyListeners(); } @override void dispose() { serialNumberController.dispose(); barcodeController.dispose(); purchasePriceController.dispose(); warrantyNumberController.dispose(); remarkController.dispose(); super.dispose(); } }