package cn.novalon.gym.manage.file.core.service.impl;

import cn.novalon.gym.manage.file.core.domain.SysFile;
import cn.novalon.gym.manage.file.core.repository.ISysFileRepository;
import cn.novalon.gym.manage.file.core.service.ISysFileService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.UUID;

@Service
public class SysFileServiceImpl implements ISysFileService {

    private final ISysFileRepository fileRepository;
    private final String uploadDir;

    public SysFileServiceImpl(
            ISysFileRepository fileRepository,
            @Value("${file.upload.dir:/tmp/uploads}") String uploadDir) {
        this.fileRepository = fileRepository;
        this.uploadDir = uploadDir;
    }

    @Override
    public Flux<SysFile> getAllFiles() {
        return fileRepository.findByDeletedAtIsNullOrderByCreatedAtDesc();
    }

    @Override
    public Mono<SysFile> getFileById(Long id) {
        return fileRepository.findById(id);
    }

    @Override
    public Flux<SysFile> getFilesByUser(String username) {
        return fileRepository.findByCreateByOrderByCreatedAtDesc(username);
    }

    @Override
    public Mono<SysFile> uploadFile(FilePart filePart, String username) {
        String originalFilename = filePart.filename();
        String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
        String newFileName = UUID.randomUUID().toString() + fileExtension;

        Path uploadPath = Paths.get(uploadDir);
        return Mono.fromCallable(() -> {
                    if (!Files.exists(uploadPath)) {
                        Files.createDirectories(uploadPath);
                    }
                    return uploadPath;
                })
                .flatMap(path -> {
                    Path filePath = path.resolve(newFileName);
                    return filePart.transferTo(filePath.toFile())
                            .thenReturn(filePath);
                })
                .flatMap(filePath -> {
                    try {
                        long fileSize = Files.size(filePath);
                        String contentType = filePart.headers().getContentType() != null 
                                ? filePart.headers().getContentType().toString() 
                                : "application/octet-stream";

                        SysFile sysFile = new SysFile();
                        sysFile.setFileName(originalFilename);
                        sysFile.setFilePath(filePath.toString());
                        sysFile.setFileSize(fileSize);
                        sysFile.setFileType(contentType);
                        sysFile.setStorageType("LOCAL");
                        sysFile.setCreateBy(username);
                        sysFile.setCreatedAt(LocalDateTime.now());

                        return fileRepository.save(sysFile);
                    } catch (IOException e) {
                        return Mono.error(e);
                    }
                });
    }

    @Override
    public Mono<Void> downloadFile(Long id) {
        return fileRepository.findById(id)
                .flatMap(file -> {
                    try {
                        Path filePath = Paths.get(file.getFilePath());
                        Files.readAllBytes(filePath);
                        return Mono.empty();
                    } catch (IOException e) {
                        return Mono.error(e);
                    }
                });
    }

    @Override
    public Mono<Void> deleteFile(Long id) {
        return fileRepository.findById(id)
                .flatMap(file -> {
                    try {
                        Path filePath = Paths.get(file.getFilePath());
                        Files.deleteIfExists(filePath);
                        return fileRepository.deleteByIdAndDeletedAtIsNull(id);
                    } catch (IOException e) {
                        return Mono.error(e);
                    }
                });
    }
}
