package com.example.pdfservice.service;

import com.example.pdfservice.config.FileStorageProperties;
import com.example.pdfservice.dto.FileResponse;
import com.example.pdfservice.entity.FileDocument;
import com.example.pdfservice.exception.FileNotFoundException;
import com.example.pdfservice.repository.FileDocumentRepository;
import jakarta.annotation.PostConstruct;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class FileStorageService {

    private final FileDocumentRepository repository;
    private final FileStorageProperties properties;
    private Path rootLocation;

    public FileStorageService(FileDocumentRepository repository, FileStorageProperties properties) {
        this.repository = repository;
        this.properties = properties;
    }

    @PostConstruct
    public void init() {
        try {
            this.rootLocation = Paths.get(properties.getStorageDir()).toAbsolutePath().normalize();
            Files.createDirectories(this.rootLocation);
        } catch (IOException e) {
            throw new RuntimeException("Could not initialize storage directory: " + properties.getStorageDir(), e);
        }
    }

    public FileResponse store(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("Cannot upload an empty file");
        }

        String originalFileName = Path.of(file.getOriginalFilename()).getFileName().toString();

        if (!originalFileName.toLowerCase().endsWith(".pdf")) {
            throw new IllegalArgumentException("Only PDF files are allowed");
        }

        String storedFileName = UUID.randomUUID() + "-" + originalFileName;

        try {
            Path destination = rootLocation.resolve(storedFileName).normalize();

            // Guard against path traversal
            if (!destination.getParent().equals(rootLocation)) {
                throw new IllegalArgumentException("Invalid file path");
            }

            Files.copy(file.getInputStream(), destination, StandardCopyOption.REPLACE_EXISTING);

            FileDocument doc = new FileDocument(
                    originalFileName,
                    storedFileName,
                    destination.toString(),
                    file.getContentType() != null ? file.getContentType() : "application/pdf",
                    file.getSize(),
                    LocalDateTime.now()
            );

            FileDocument saved = repository.save(doc);
            return FileResponse.fromEntity(saved);

        } catch (IOException e) {
            throw new RuntimeException("Failed to store file " + originalFileName, e);
        }
    }

    public List<FileResponse> listAll() {
        return repository.findAll().stream()
                .map(FileResponse::fromEntity)
                .collect(Collectors.toList());
    }

    public FileDocument getMetadata(Long id) {
        return repository.findById(id)
                .orElseThrow(() -> new FileNotFoundException("No file found with id " + id));
    }

    /**
     * Loads the actual PDF bytes from disk using the path stored in the DB.
     */
    public Resource loadAsResource(Long id) {
        FileDocument doc = getMetadata(id);
        try {
            Path filePath = Paths.get(doc.getFilePath()).normalize();
            Resource resource = new UrlResource(filePath.toUri());

            if (!resource.exists() || !resource.isReadable()) {
                throw new FileNotFoundException("File not readable on disk: " + doc.getFilePath());
            }
            return resource;
        } catch (MalformedURLException e) {
            throw new FileNotFoundException("Could not read file with id " + id, e);
        }
    }

    public void delete(Long id) {
        FileDocument doc = getMetadata(id);
        try {
            Files.deleteIfExists(Paths.get(doc.getFilePath()));
        } catch (IOException e) {
            throw new RuntimeException("Failed to delete file from disk", e);
        }
        repository.delete(doc);
    }
}
