import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:injectable/injectable.dart'; import 'package:superport/core/constants/api_endpoints.dart'; import 'package:superport/core/errors/exceptions.dart'; import 'package:superport/data/datasources/remote/api_client.dart'; import 'package:superport/data/models/common/api_response.dart'; import 'package:superport/data/models/common/paginated_response.dart'; import 'package:superport/data/models/company/company_dto.dart'; import 'package:superport/data/models/company/company_list_dto.dart'; import 'package:superport/data/models/company/branch_dto.dart'; import 'package:superport/data/models/company/company_branch_flat_dto.dart'; abstract class CompanyRemoteDataSource { Future> getCompanies({ int page = 1, int perPage = 20, String? search, bool? isActive, bool includeInactive = false, }); Future createCompany(CreateCompanyRequest request); Future getCompanyDetail(int id); Future getCompanyWithBranches(int id); Future updateCompany(int id, UpdateCompanyRequest request); Future deleteCompany(int id); Future> getCompanyNames(); Future> getCompaniesWithBranches(); Future checkDuplicateCompany(String name); Future> searchCompanies(String query); Future updateCompanyStatus(int id, bool isActive); // Branch related methods Future createBranch(int companyId, CreateBranchRequest request); Future getBranchDetail(int companyId, int branchId); Future updateBranch(int companyId, int branchId, UpdateBranchRequest request); Future deleteBranch(int companyId, int branchId); Future> getCompanyBranches(int companyId); Future> getCompanyBranchesFlat(); } @LazySingleton(as: CompanyRemoteDataSource) class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource { final ApiClient _apiClient; CompanyRemoteDataSourceImpl(this._apiClient); @override Future> getCompanies({ int page = 1, int perPage = 20, String? search, bool? isActive, bool includeInactive = false, }) async { try { final queryParams = { 'page': page, 'per_page': perPage, if (search != null) 'search': search, if (isActive != null) 'is_active': isActive, 'include_inactive': includeInactive, }; final response = await _apiClient.get( ApiEndpoints.companies, queryParameters: queryParams, ); if (response.statusCode == 200) { // API 응답을 직접 파싱 final responseData = response.data; if (responseData != null && responseData['success'] == true && responseData['data'] != null) { final List dataList = responseData['data']; final pagination = responseData['pagination'] ?? {}; // CompanyListDto로 변환 final items = dataList.map((item) => CompanyListDto.fromJson(item as Map)).toList(); // PaginatedResponse 생성 return PaginatedResponse( items: items, page: pagination['page'] ?? page, size: pagination['per_page'] ?? perPage, totalElements: pagination['total'] ?? 0, totalPages: pagination['total_pages'] ?? 1, first: (pagination['page'] ?? page) == 1, last: (pagination['page'] ?? page) == (pagination['total_pages'] ?? 1), ); } else { throw ApiException( message: responseData?['error']?['message'] ?? 'Failed to load companies', statusCode: response.statusCode, ); } } else { throw ApiException( message: 'Failed to load companies', statusCode: response.statusCode, ); } } catch (e) { if (e is ApiException) rethrow; throw ApiException(message: e.toString()); } } @override Future createCompany(CreateCompanyRequest request) async { try { debugPrint('[CompanyRemoteDataSource] Sending POST request to ${ApiEndpoints.companies}'); debugPrint('[CompanyRemoteDataSource] Request data: ${request.toJson()}'); final response = await _apiClient.post( ApiEndpoints.companies, data: request.toJson(), ); debugPrint('[CompanyRemoteDataSource] Response status: ${response.statusCode}'); debugPrint('[CompanyRemoteDataSource] Response data: ${response.data}'); if (response.statusCode == 201 || response.statusCode == 200) { // API 응답 구조 확인 final responseData = response.data; if (responseData != null && responseData['success'] == true && responseData['data'] != null) { // 직접 파싱 return CompanyResponse.fromJson(responseData['data'] as Map); } else { // ApiResponse 형식으로 파싱 시도 final apiResponse = ApiResponse.fromJson( response.data, (json) => CompanyResponse.fromJson(json as Map), ); return apiResponse.data!; } } else { throw ApiException( message: 'Failed to create company - Status: ${response.statusCode}', statusCode: response.statusCode, ); } } catch (e, stackTrace) { debugPrint('[CompanyRemoteDataSource] Error creating company: $e'); debugPrint('[CompanyRemoteDataSource] Stack trace: $stackTrace'); if (e is ApiException) rethrow; throw ApiException(message: 'Error creating company: $e'); } } @override Future getCompanyDetail(int id) async { try { final response = await _apiClient.dio.get( '${ApiEndpoints.companies}/$id', ); return CompanyResponse.fromJson(response.data['data']); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to fetch company detail', statusCode: e.response?.statusCode, ); } } @override Future getCompanyWithBranches(int id) async { try { final response = await _apiClient.dio.get( '${ApiEndpoints.companies}/$id/with-branches', ); return CompanyWithBranches.fromJson(response.data['data']); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to fetch company with branches', statusCode: e.response?.statusCode, ); } } @override Future updateCompany(int id, UpdateCompanyRequest request) async { try { final response = await _apiClient.dio.put( '${ApiEndpoints.companies}/$id', data: request.toJson(), ); return CompanyResponse.fromJson(response.data['data']); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to update company', statusCode: e.response?.statusCode, ); } } @override Future deleteCompany(int id) async { try { await _apiClient.dio.delete( '${ApiEndpoints.companies}/$id', ); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to delete company', statusCode: e.response?.statusCode, ); } } @override Future> getCompanyNames() async { try { final response = await _apiClient.dio.get( '${ApiEndpoints.companies}/names', ); final List data = response.data['data']; return data.map((json) => CompanyNameDto.fromJson(json)).toList(); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to fetch company names', statusCode: e.response?.statusCode, ); } } // Branch methods @override Future createBranch(int companyId, CreateBranchRequest request) async { try { final response = await _apiClient.dio.post( '${ApiEndpoints.companies}/$companyId/branches', data: request.toJson(), ); return BranchResponse.fromJson(response.data['data']); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to create branch', statusCode: e.response?.statusCode, ); } } @override Future getBranchDetail(int companyId, int branchId) async { try { final response = await _apiClient.dio.get( '${ApiEndpoints.companies}/$companyId/branches/$branchId', ); return BranchResponse.fromJson(response.data['data']); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to fetch branch detail', statusCode: e.response?.statusCode, ); } } @override Future updateBranch(int companyId, int branchId, UpdateBranchRequest request) async { try { final response = await _apiClient.dio.put( '${ApiEndpoints.companies}/$companyId/branches/$branchId', data: request.toJson(), ); return BranchResponse.fromJson(response.data['data']); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to update branch', statusCode: e.response?.statusCode, ); } } @override Future deleteBranch(int companyId, int branchId) async { try { await _apiClient.dio.delete( '${ApiEndpoints.companies}/$companyId/branches/$branchId', ); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to delete branch', statusCode: e.response?.statusCode, ); } } @override Future> getCompanyBranches(int companyId) async { try { final response = await _apiClient.dio.get( '${ApiEndpoints.companies}/$companyId/branches', ); final List data = response.data['data']; return data.map((json) => BranchListDto.fromJson(json)).toList(); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to fetch company branches', statusCode: e.response?.statusCode, ); } } @override Future> getCompaniesWithBranches() async { try { final response = await _apiClient.get('${ApiEndpoints.companies}/branches'); if (response.statusCode == 200) { final apiResponse = ApiResponse>.fromJson( response.data, (json) => (json as List) .map((item) => CompanyWithBranches.fromJson(item)) .toList(), ); return apiResponse.data!; } else { throw ApiException( message: 'Failed to load companies with branches', statusCode: response.statusCode, ); } } catch (e) { if (e is ApiException) rethrow; throw ApiException(message: e.toString()); } } @override Future> getCompanyBranchesFlat() async { try { final response = await _apiClient.get('${ApiEndpoints.companies}/branches'); if (response.statusCode == 200) { final responseData = response.data; if (responseData != null && responseData['success'] == true && responseData['data'] != null) { final List dataList = responseData['data']; return dataList.map((item) => CompanyBranchFlatDto.fromJson(item as Map) ).toList(); } else { throw ApiException( message: responseData?['error']?['message'] ?? 'Failed to load company branches', statusCode: response.statusCode, ); } } else { throw ApiException( message: 'Failed to load company branches', statusCode: response.statusCode, ); } } catch (e) { if (e is ApiException) rethrow; throw ApiException(message: e.toString()); } } @override Future checkDuplicateCompany(String name) async { try { final response = await _apiClient.get( '${ApiEndpoints.companies}/check-duplicate', queryParameters: {'name': name}, ); if (response.statusCode == 200) { final apiResponse = ApiResponse>.fromJson( response.data, (json) => json as Map, ); return apiResponse.data?['exists'] ?? false; } else { throw ApiException( message: 'Failed to check duplicate', statusCode: response.statusCode, ); } } catch (e) { if (e is ApiException) rethrow; throw ApiException(message: e.toString()); } } @override Future> searchCompanies(String query) async { try { final response = await _apiClient.get( '${ApiEndpoints.companies}/search', queryParameters: {'q': query}, ); if (response.statusCode == 200) { final apiResponse = ApiResponse>.fromJson( response.data, (json) => (json as List) .map((item) => CompanyListDto.fromJson(item)) .toList(), ); return apiResponse.data!; } else { throw ApiException( message: 'Failed to search companies', statusCode: response.statusCode, ); } } catch (e) { if (e is ApiException) rethrow; throw ApiException(message: e.toString()); } } @override Future updateCompanyStatus(int id, bool isActive) async { try { final response = await _apiClient.patch( '${ApiEndpoints.companies}/$id/status', data: {'is_active': isActive}, ); if (response.statusCode != 200) { throw ApiException( message: 'Failed to update company status', statusCode: response.statusCode, ); } } catch (e) { if (e is ApiException) rethrow; throw ApiException(message: e.toString()); } } }