I want to make the adjustments:
1. Make uploading of image not compulsory.
2. Users can upload image afterwards, and it will update the same document, not create a new one.
LeaveRequestRepository:
package com.g4t4.home_cleaning_scheduler.repository;

import com.g4t4.home_cleaning_scheduler.model.LeaveRequest;
import com.google.cloud.firestore.CollectionReference;
import com.google.cloud.firestore.Firestore;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class LeaveRequestRepository extends AbstractFirestoreRepository<LeaveRequest> {

    @Autowired
    public LeaveRequestRepository(Firestore firestore) {
        super(firestore, "leave_requests");
    }

    public boolean saveLeaveRequest(LeaveRequest leaveRequest) {
        return save(leaveRequest);
    }

    public void deleteLeaveRequest(LeaveRequest leaveRequest) {
        delete(leaveRequest);
    }

    public CollectionReference getCollectionReference() {
        return super.getCollectionReference();
    }

    // Additional methods to get leave requests by workerId, supervisorId, etc., can be added if needed.
}

LeaveRequestService:
package com.g4t4.home_cleaning_scheduler.service;

import com.g4t4.home_cleaning_scheduler.model.LeaveRequest;
import com.g4t4.home_cleaning_scheduler.model.User;
import com.g4t4.home_cleaning_scheduler.repository.LeaveRequestRepository;
import com.g4t4.home_cleaning_scheduler.repository.UserRepository;
import com.google.api.core.ApiFuture;
import com.google.cloud.firestore.CollectionReference;
import com.google.cloud.firestore.Query;
import com.google.cloud.firestore.QuerySnapshot;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.StorageOptions;
import com.google.firebase.cloud.StorageClient;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.google.cloud.storage.Acl;

import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

@Service
public class LeaveRequestService {

    private final LeaveRequestRepository leaveRequestRepository;

    @Autowired
    private UserRepository userRepository;

    public LeaveRequestService(LeaveRequestRepository leaveRequestRepository) {
        this.leaveRequestRepository = leaveRequestRepository;
    }

    public boolean submitLeaveRequest(LeaveRequest leaveRequest, MultipartFile medicalCertificateFile) throws IOException {

        if (leaveRequest.getLeaveType().equalsIgnoreCase("Medical")) {
            if (medicalCertificateFile != null && !medicalCertificateFile.isEmpty()) {
                // Upload the image to Firebase Storage
                String imageUrl = uploadMedicalCertificate(medicalCertificateFile);
                leaveRequest.setMedicalCertificateUrl(imageUrl);
            }
        }

        // Generate a unique ID for the leave request
        String leaveRequestId = UUID.randomUUID().toString();
        leaveRequest.setLeaveRequestId(leaveRequestId);

        return leaveRequestRepository.saveLeaveRequest(leaveRequest);
    }

    public boolean uploadMedicalCertificate(String leaveRequestId, MultipartFile medicalCertificateFile) throws IOException, ExecutionException, InterruptedException {
        if (medicalCertificateFile == null || medicalCertificateFile.isEmpty()) {
            throw new IllegalArgumentException("Medical certificate image is required.");
        }
        // Retrieve existing leave request
        Optional<LeaveRequest> optionalLeaveRequest = leaveRequestRepository.get(leaveRequestId);
        if (!optionalLeaveRequest.isPresent()) {
            throw new IllegalArgumentException("Leave request not found.");
        }

        LeaveRequest leaveRequest = optionalLeaveRequest.get();

        // Upload the image
        String imageUrl = uploadMedicalCertificate(medicalCertificateFile);
        leaveRequest.setMedicalCertificateUrl(imageUrl);
        // Update the leave request
        return leaveRequestRepository.saveLeaveRequest(leaveRequest);
    }

    public boolean updateLeaveRequestStatus(String leaveRequestId, String adminId, String status) throws ExecutionException, InterruptedException {
        // Retrieve the leave request
        Optional<LeaveRequest> optionalLeaveRequest = leaveRequestRepository.get(leaveRequestId);
        if (!optionalLeaveRequest.isPresent()) {
            throw new IllegalArgumentException("Leave request not found.");
        }
        LeaveRequest leaveRequest = optionalLeaveRequest.get();

        // Check if the admin is the supervisor of the worker
        if (!leaveRequest.getSupervisorId().equals(adminId)) {
            throw new IllegalArgumentException("Admin is not authorized to update this leave request.");
        }

        leaveRequest.setStatus(status);
        return leaveRequestRepository.saveLeaveRequest(leaveRequest);
    }

    public boolean approveLeaveRequest(String leaveRequestId, String adminId) throws ExecutionException, InterruptedException {
        return updateLeaveRequestStatus(leaveRequestId, adminId, "Approved");
    }

    public boolean rejectLeaveRequest(String leaveRequestId, String adminId) throws ExecutionException, InterruptedException {
        return updateLeaveRequestStatus(leaveRequestId, adminId, "Rejected");
    }

    private String uploadMedicalCertificate(MultipartFile file) throws IOException {
        String bucketName = "clean-affcc.appspot.com"; // Replace with your bucket name
        String folderName = "medical_certificates";

        String fileName = folderName + "/" + UUID.randomUUID().toString() + "_" + file.getOriginalFilename();

        // Build the blob info with public read ACL
        BlobInfo blobInfo = BlobInfo.newBuilder(bucketName, fileName)
                .setContentType(file.getContentType())
                .setAcl(
                        new java.util.ArrayList<>(
                                java.util.Arrays.asList(
                                        Acl.of(Acl.User.ofAllUsers(), Acl.Role.READER))))
                .build();

        // Upload to Cloud Storage
        StorageClient.getInstance().bucket(bucketName).create(blobInfo.getName(), file.getBytes(), blobInfo.getContentType());

        // Return the public URL to the uploaded file
        return String.format("https://storage.googleapis.com/%s/%s", bucketName, fileName);
    }

    public List<LeaveRequest> getLeaveRequestsBySupervisor(String supervisorId) throws ExecutionException, InterruptedException {
        CollectionReference collection = leaveRequestRepository.getCollectionReference();
        Query query = collection.whereEqualTo("supervisorId", supervisorId);

        ApiFuture<QuerySnapshot> querySnapshot = query.get();

        List<LeaveRequest> leaveRequests = querySnapshot.get().getDocuments().stream()
                .map(document -> document.toObject(LeaveRequest.class))
                .collect(Collectors.toList());

        return leaveRequests;
    }

    public List<LeaveRequest> getLeaveRequestsByWorker(String workerId) throws ExecutionException, InterruptedException {
        CollectionReference collection = leaveRequestRepository.getCollectionReference();
        Query query = collection.whereEqualTo("workerId", workerId);

        ApiFuture<QuerySnapshot> querySnapshot = query.get();

        List<LeaveRequest> leaveRequests = querySnapshot.get().getDocuments().stream()
                .map(document -> document.toObject(LeaveRequest.class))
                .collect(Collectors.toList());

        return leaveRequests;
    }

    public List<LeaveRequest> getApprovedLeaveRequests() throws ExecutionException, InterruptedException {
        CollectionReference collection = leaveRequestRepository.getCollectionReference();
        Query query = collection.whereEqualTo("status", "Approved");

        ApiFuture<QuerySnapshot> querySnapshot = query.get();

        List<LeaveRequest> leaveRequests = querySnapshot.get().getDocuments().stream()
                .map(document -> document.toObject(LeaveRequest.class))
                .collect(Collectors.toList());

        return leaveRequests;
    }

    @Scheduled(cron = "0 0 0 * * *") // Runs daily at midnight
    public void notifyAdminsForPendingMedicalCertificates() throws ExecutionException, InterruptedException {
        CollectionReference collection = leaveRequestRepository.getCollectionReference();
        Query query = collection
                .whereEqualTo("leaveType", "Medical")
                .whereEqualTo("status", "Pending")
                .whereEqualTo("medicalCertificateUrl", null);

        ApiFuture<QuerySnapshot> querySnapshot = query.get();

        List<LeaveRequest> leaveRequests = querySnapshot.get().getDocuments().stream()
                .map(document -> document.toObject(LeaveRequest.class))
                .collect(Collectors.toList());

        for (LeaveRequest leaveRequest : leaveRequests) {
            // Get supervisor/admin information
            String supervisorId = leaveRequest.getSupervisorId();
            User supervisor = userRepository.findUserById(supervisorId);

            if (supervisor != null) {
                // Send notification to supervisor
                // Implement your notification logic here (e.g., send email)
                System.out.println("Sending notification to admin: " + supervisor.getEmail());
                // Placeholder for notification logic
            }
        }
    }
}

LeaveRequestController:
package com.g4t4.home_cleaning_scheduler.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.g4t4.home_cleaning_scheduler.model.LeaveRequest;
import com.g4t4.home_cleaning_scheduler.service.LeaveRequestService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;

@RestController
@RequestMapping("/api/leave-requests")
public class LeaveRequestController {

    @Autowired
    private LeaveRequestService leaveRequestService;

    @PostMapping(consumes = "multipart/form-data")
    public ResponseEntity<String> submitLeaveRequest(@RequestParam("leaveRequest") String leaveRequestJson,
                                                     @RequestParam(value = "medicalCertificate", required = false) MultipartFile medicalCertificate) {
        try {
            // Convert leaveRequestJson to LeaveRequest object
            ObjectMapper objectMapper = new ObjectMapper();
            LeaveRequest leaveRequest = objectMapper.readValue(leaveRequestJson, LeaveRequest.class);
            leaveRequestService.submitLeaveRequest(leaveRequest, medicalCertificate);
            return ResponseEntity.status(HttpStatus.CREATED).body("Leave request submitted successfully.");
        } catch (IllegalArgumentException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error processing leave request.");
        }
    }

    @PutMapping("/{leaveRequestId}/upload-medical-certificate")
    public ResponseEntity<String> uploadMedicalCertificate(@PathVariable String leaveRequestId,
                                                           @RequestParam("medicalCertificate") MultipartFile medicalCertificate) {
        try {
            leaveRequestService.uploadMedicalCertificate(leaveRequestId, medicalCertificate);
            return ResponseEntity.ok("Medical certificate uploaded successfully.");
        } catch (IllegalArgumentException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
        } catch (IOException | ExecutionException | InterruptedException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error uploading medical certificate.");
        }
    }

    @PostMapping("/{leaveRequestId}/approve")
    public ResponseEntity<String> approveLeaveRequest(@PathVariable String leaveRequestId, @RequestParam("adminId") String adminId) {
        try {
            leaveRequestService.approveLeaveRequest(leaveRequestId, adminId);
            return ResponseEntity.ok("Leave request approved successfully.");
        } catch (IllegalArgumentException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
        } catch (ExecutionException | InterruptedException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error approving leave request.");
        }
    }

    @PostMapping("/{leaveRequestId}/reject")
    public ResponseEntity<String> rejectLeaveRequest(@PathVariable String leaveRequestId, @RequestParam("adminId") String adminId) {
        try {
            leaveRequestService.rejectLeaveRequest(leaveRequestId, adminId);
            return ResponseEntity.ok("Leave request rejected successfully.");
        } catch (IllegalArgumentException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
        } catch (ExecutionException | InterruptedException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error rejecting leave request.");
        }
    }

    @GetMapping("/supervisor/{supervisorId}")
    public ResponseEntity<List<LeaveRequest>> getLeaveRequestsBySupervisor(@PathVariable String supervisorId) {
        try {
            List<LeaveRequest> leaveRequests = leaveRequestService.getLeaveRequestsBySupervisor(supervisorId);
            return ResponseEntity.ok(leaveRequests);
        } catch (ExecutionException | InterruptedException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    @GetMapping("/worker/{workerId}")
    public ResponseEntity<List<LeaveRequest>> getLeaveRequestsByWorker(@PathVariable String workerId) {
        try {
            List<LeaveRequest> leaveRequests = leaveRequestService.getLeaveRequestsByWorker(workerId);
            return ResponseEntity.ok(leaveRequests);
        } catch (ExecutionException | InterruptedException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    @GetMapping("/approved")
    public ResponseEntity<List<LeaveRequest>> getApprovedLeaveRequests() {
        try {
            List<LeaveRequest> leaveRequests = leaveRequestService.getApprovedLeaveRequests();
            return ResponseEntity.ok(leaveRequests);
        } catch (ExecutionException | InterruptedException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}