import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; import 'package:marco/helpers/services/app_logger.dart'; import 'package:marco/controller/my_controller.dart'; import 'package:marco/controller/task_Planning/daily_task_Planning_controller.dart'; import 'package:marco/helpers/services/api_service.dart'; import 'package:marco/helpers/widgets/my_form_validator.dart'; import 'package:marco/helpers/widgets/my_image_compressor.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/model/dailyTaskPlanning/work_status_model.dart'; enum ApiStatus { idle, loading, success, failure } class ReportTaskActionController extends MyController { final RxBool isLoading = false.obs; final Rx reportStatus = ApiStatus.idle.obs; final Rx commentStatus = ApiStatus.idle.obs; final RxList selectedImages = [].obs; final RxList workStatus = [].obs; final RxList workStatuses = [].obs; final RxBool showAddTaskCheckbox = false.obs; final RxBool isAddTaskChecked = false.obs; final RxBool isLoadingWorkStatus = false.obs; final Rxn> selectedTask = Rxn>(); final RxString selectedWorkStatusName = ''.obs; final MyFormValidator basicValidator = MyFormValidator(); final DailyTaskPlanningController taskController = Get.put(DailyTaskPlanningController()); final ImagePicker _picker = ImagePicker(); final assignedDateController = TextEditingController(); final workAreaController = TextEditingController(); final activityController = TextEditingController(); final teamSizeController = TextEditingController(); final taskIdController = TextEditingController(); final assignedController = TextEditingController(); final completedWorkController = TextEditingController(); final commentController = TextEditingController(); final assignedByController = TextEditingController(); final teamMembersController = TextEditingController(); final plannedWorkController = TextEditingController(); final approvedTaskController = TextEditingController(); List get _allControllers => [ assignedDateController, workAreaController, activityController, teamSizeController, taskIdController, assignedController, completedWorkController, commentController, assignedByController, teamMembersController, plannedWorkController, approvedTaskController, ]; @override void onInit() { super.onInit(); logSafe("Initializing ReportTaskController..."); _initializeFormFields(); } @override void onClose() { for (final controller in _allControllers) { controller.dispose(); } logSafe("Disposed all text controllers in ReportTaskActionController."); super.onClose(); } void _initializeFormFields() { basicValidator ..addField('assigned_date', label: "Assigned Date", controller: assignedDateController) ..addField('work_area', label: "Work Area", controller: workAreaController) ..addField('activity', label: "Activity", controller: activityController) ..addField('team_size', label: "Team Size", controller: teamSizeController) ..addField('task_id', label: "Task Id", controller: taskIdController) ..addField('assigned', label: "Assigned", controller: assignedController) ..addField('completed_work', label: "Completed Work", required: true, controller: completedWorkController) ..addField('comment', label: "Comment", required: true, controller: commentController) ..addField('assigned_by', label: "Assigned By", controller: assignedByController) ..addField('team_members', label: "Team Members", controller: teamMembersController) ..addField('planned_work', label: "Planned Work", controller: plannedWorkController) ..addField('approved_task', label: "Approved Task", required: true, controller: approvedTaskController); } Future approveTask({ required String projectId, required String comment, required String reportActionId, required String approvedTaskCount, List? images, }) async { logSafe("approveTask() started", sensitive: false); if (projectId.isEmpty || reportActionId.isEmpty) { _showError("Project ID and Report Action ID are required."); logSafe("Missing required projectId or reportActionId", level: LogLevel.warning); return false; } final approvedTaskInt = int.tryParse(approvedTaskCount); final completedWorkInt = int.tryParse(completedWorkController.text.trim()); if (approvedTaskInt == null) { _showError("Invalid approved task count."); logSafe("Invalid approvedTaskCount: $approvedTaskCount", level: LogLevel.warning); return false; } if (completedWorkInt != null && approvedTaskInt > completedWorkInt) { _showError("Approved task count cannot exceed completed work."); logSafe("Validation failed: approved > completed", level: LogLevel.warning); return false; } if (comment.trim().isEmpty) { _showError("Comment is required."); logSafe("Comment field is empty", level: LogLevel.warning); return false; } try { reportStatus.value = ApiStatus.loading; isLoading.value = true; logSafe("Calling _prepareImages() for approval..."); final imageData = await _prepareImages(images); logSafe("Calling ApiService.approveTask()"); final success = await ApiService.approveTask( id: projectId, workStatus: reportActionId, approvedTask: approvedTaskInt, comment: comment, images: imageData, ); if (success) { logSafe("Task approved successfully"); _showSuccess("Task approved successfully!"); await taskController.fetchTaskData(projectId); return true; } else { logSafe("API returned failure on approveTask", level: LogLevel.error); _showError("Failed to approve task."); return false; } } catch (e, st) { logSafe("Error in approveTask: $e", level: LogLevel.error, error: e, stackTrace: st); _showError("An error occurred."); return false; } finally { isLoading.value = false; Future.delayed(const Duration(milliseconds: 500), () { reportStatus.value = ApiStatus.idle; }); } } Future commentTask({ required String projectId, required String comment, List? images, }) async { logSafe("commentTask() started", sensitive: false); if (commentController.text.trim().isEmpty) { _showError("Comment is required."); logSafe("Comment field is empty", level: LogLevel.warning); return; } try { isLoading.value = true; logSafe("Calling _prepareImages() for comment..."); final imageData = await _prepareImages(images); logSafe("Calling ApiService.commentTask()"); final success = await ApiService.commentTask( id: projectId, comment: commentController.text.trim(), images: imageData, ).timeout(const Duration(seconds: 30), onTimeout: () { throw Exception("Request timed out."); }); if (success) { logSafe("Comment added successfully"); _showSuccess("Task commented successfully!"); await taskController.fetchTaskData(projectId); } else { logSafe("API returned failure on commentTask", level: LogLevel.error); _showError("Failed to comment task."); } } catch (e, st) { logSafe("Error in commentTask: $e", level: LogLevel.error, error: e, stackTrace: st); _showError("An error occurred while commenting the task."); } finally { isLoading.value = false; } } Future fetchWorkStatuses() async { logSafe("Fetching work statuses..."); isLoadingWorkStatus.value = true; final response = await ApiService.getWorkStatus(); if (response != null) { final model = WorkStatusResponseModel.fromJson(response); workStatus.assignAll(model.data); logSafe("Fetched ${model.data.length} work statuses"); } else { logSafe("No work statuses found or API call failed", level: LogLevel.warning); } isLoadingWorkStatus.value = false; update(['dashboard_controller']); } Future>?> _prepareImages(List? images) async { if (images == null || images.isEmpty) { logSafe("_prepareImages: No images selected."); return null; } logSafe("_prepareImages: Compressing and encoding images..."); final results = await Future.wait(images.map((file) async { final compressedBytes = await compressImageToUnder100KB(file); if (compressedBytes == null) return null; return { "fileName": file.path.split('/').last, "base64Data": base64Encode(compressedBytes), "contentType": _getContentTypeFromFileName(file.path), "fileSize": compressedBytes.lengthInBytes, "description": "Image uploaded for task", }; })); logSafe("_prepareImages: Prepared ${results.whereType>().length} images."); return results.whereType>().toList(); } String _getContentTypeFromFileName(String fileName) { final ext = fileName.split('.').last.toLowerCase(); return switch (ext) { 'jpg' || 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'webp' => 'image/webp', 'gif' => 'image/gif', _ => 'application/octet-stream', }; } Future pickImages({required bool fromCamera}) async { logSafe("Opening image picker..."); if (fromCamera) { final pickedFile = await _picker.pickImage(source: ImageSource.camera, imageQuality: 75); if (pickedFile != null) { selectedImages.add(File(pickedFile.path)); logSafe("Image added from camera: ${pickedFile.path}", ); } } else { final pickedFiles = await _picker.pickMultiImage(imageQuality: 75); selectedImages.addAll(pickedFiles.map((xfile) => File(xfile.path))); logSafe("${pickedFiles.length} images added from gallery.", ); } } void removeImageAt(int index) { if (index >= 0 && index < selectedImages.length) { logSafe("Removing image at index $index", ); selectedImages.removeAt(index); } } void _showError(String message) => showAppSnackbar( title: "Error", message: message, type: SnackbarType.error); void _showSuccess(String message) => showAppSnackbar( title: "Success", message: message, type: SnackbarType.success); }