package mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.service.impl;

import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.models.applications.DTO.ApplicationDetailsDTO;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.models.applications.DTO.ApplicationStatusDTO;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.models.enumerations.ApplicationStatus;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.models.users.JobSeeker;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.repositories.JobSeekerRepository;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.models.applications.Application;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.models.applications.DTO.ApplicationDTO;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.models.job_advertisements.JobAdvertisement;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.models.users.User;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.repositories.ApplicationRepository;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.repositories.JobAdvertisementRepository;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.repositories.UserRepository;
import mk.ukim.finki.predmeti.internettehnologii.jobvistabackend.service.intef.ApplicationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;

@Service
//@RequiredArgsConstructor
public class ApplicationServiceImpl implements ApplicationService {
    private final Path fileStorageLocation;

    private final UserRepository userRepository;
    private final ApplicationRepository applicationRepository;
    private final JobAdvertisementRepository jobAdvertisementRepository;
    private final JobSeekerRepository jobSeekerRepository;

    @Autowired
    public ApplicationServiceImpl(@Value("${file.upload-dir}") String uploadDir, UserRepository userRepository, ApplicationRepository applicationRepository, JobAdvertisementRepository jobAdvertisementRepository,
                                  JobSeekerRepository jobSeekerRepository) {
        this.fileStorageLocation = Paths.get(uploadDir + "/applications").toAbsolutePath().normalize();

        try {
            Files.createDirectories(this.fileStorageLocation);
        } catch (IOException ex) {
            throw new RuntimeException("Could not create the directory where the uploaded files will be stored.", ex);
        }

        this.userRepository = userRepository;
        this.applicationRepository = applicationRepository;
        this.jobAdvertisementRepository = jobAdvertisementRepository;
        this.jobSeekerRepository = jobSeekerRepository;
    }

    @Override
    public ApplicationDetailsDTO submitApplication(ApplicationDTO applicationDTO) {

        JobSeeker jobSeeker = jobSeekerRepository.findById(applicationDTO.getJobSeekerId())
                .orElseThrow(() -> new IllegalArgumentException("User not found."));
        JobAdvertisement jobAdvertisement = jobAdvertisementRepository.findById(applicationDTO.getJobAdId())
                .orElseThrow(() -> new IllegalArgumentException("Job advertisement not found."));

        if (applicationDTO.getResumeFile().isEmpty()) {
            throw new RuntimeException("Failed to store empty file.");
        }

        List<String> answers = new ArrayList<>();
        answers.add(applicationDTO.getAnswerOne());
        answers.add(applicationDTO.getAnswerTwo());
        answers.add(applicationDTO.getAnswerThree());

        Application application = new Application(jobSeeker, jobAdvertisement,
                answers,applicationDTO.getMessageToRecruiter());
        application = applicationRepository.save(application);

        Path filePath = this.fileStorageLocation.resolve(String.valueOf(application.getId())).resolve("resume");
        Path targetLocation = filePath.resolve(applicationDTO.getResumeFile().getOriginalFilename());

        try {
            Files.createDirectories(filePath);
            Files.copy(applicationDTO.getResumeFile().getInputStream(), targetLocation);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        String relativePath = Paths.get("uploads","applications",String.valueOf(application.getId()),
                "resume", applicationDTO.getResumeFile().getOriginalFilename()).toString();
        application.setResumeFilePath(relativePath);
        application = applicationRepository.save(application);

        return Application.mapToApplicationDetailsDTO(application);
    }

    @Override
    public List<ApplicationDetailsDTO> findAllByJobAdvertisementId(Long jobId) {
        List<Application> applications =  applicationRepository.findAllByJobAdvertisementId(jobId);
        return applications.stream().map(Application::mapToApplicationDetailsDTO).toList();
    }

    @Override
    public List<ApplicationDetailsDTO> findAllByJobSeekerId(Long jobSeekerId) {
       List<Application> applications = applicationRepository.findAllByJobSeekerId(jobSeekerId);
       return applications.stream().map(Application::mapToApplicationDetailsDTO).toList();
    }

    @Override
    public Resource loadResumeAsResource(Long applicationId) {
        Application application = applicationRepository.findById(applicationId).
                orElseThrow(() -> new IllegalArgumentException("Application not found"));

        String relativeFilePath = application.getResumeFilePath();
        Path filePath = fileStorageLocation.getParent().getParent().resolve(relativeFilePath).normalize();

        try {
            Resource resource = new UrlResource(filePath.toUri());
            if (resource.exists()) {
                return resource;
            } else {
                throw new RuntimeException("File not found: " + relativeFilePath);
            }
        } catch (IOException ex) {
            throw new RuntimeException("File path is invalid: " + relativeFilePath, ex);
        }
    }

    @Override
    public ApplicationStatusDTO updateApplicationStatus(Long id, String status) {
       Application application = applicationRepository.findById(id).orElse(null);
        System.out.println(status);
       application.setStatus(ApplicationStatus.valueOf(status));
       applicationRepository.save(application);
       return new ApplicationStatusDTO(id, status);
    }
}
