package kr.iotdoor.boot.service;

import kr.iotdoor.boot.config.IotdoorProperties;
import kr.iotdoor.boot.mapper.AtchFileDetailMapper;
import kr.iotdoor.boot.mapper.AtchFileMapper;
import kr.iotdoor.boot.model.file.StoredFile;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

@Service
public class FileStorageService {

    private static final DateTimeFormatter DATE_PATH_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd");

    private final IotdoorProperties properties;
    private final AtchFileMapper atchFileMapper;
    private final AtchFileDetailMapper atchFileDetailMapper;

    public FileStorageService(
        IotdoorProperties properties,
        AtchFileMapper atchFileMapper,
        AtchFileDetailMapper atchFileDetailMapper
    ) {
        this.properties = properties;
        this.atchFileMapper = atchFileMapper;
        this.atchFileDetailMapper = atchFileDetailMapper;
    }

    @Transactional
    public StoredFile storeSingleFile(MultipartFile multipartFile, String directory, String description) {
        if (multipartFile == null || multipartFile.isEmpty()) {
            throw new IllegalArgumentException("업로드할 파일이 없습니다.");
        }

        String atchFileIdntfNo = UUID.randomUUID().toString().replace("-", "");
        atchFileMapper.insertAtchFile(atchFileIdntfNo);

        String sanitizedDirectory = sanitizeDirectory(directory);
        String relativeDirectory = sanitizedDirectory + "/" + LocalDate.now().format(DATE_PATH_FORMATTER);
        Path rootPath = Paths.get(properties.getFile().getUploadPath()).toAbsolutePath().normalize();
        Path storageDirectory = rootPath.resolve(relativeDirectory).normalize();

        if (!storageDirectory.startsWith(rootPath)) {
            throw new IllegalArgumentException("허용되지 않은 업로드 경로입니다.");
        }

        try {
            Files.createDirectories(storageDirectory);
        } catch (IOException exception) {
            throw new IllegalStateException("업로드 디렉터리를 생성할 수 없습니다.", exception);
        }

        String originalFileName = StringUtils.cleanPath(multipartFile.getOriginalFilename());
        String extension = extractExtension(originalFileName);
        String storedFileName = UUID.randomUUID().toString().replace("-", "");
        if (StringUtils.hasText(extension)) {
            storedFileName += "." + extension;
        }

        Path targetPath = storageDirectory.resolve(storedFileName).normalize();
        if (!targetPath.startsWith(rootPath)) {
            throw new IllegalArgumentException("허용되지 않은 파일 저장 위치입니다.");
        }

        try {
            multipartFile.transferTo(targetPath);
        } catch (IOException exception) {
            throw new IllegalStateException("파일 저장에 실패했습니다.", exception);
        }

        StoredFile storedFile = new StoredFile();
        storedFile.setAtchFileIdntfNo(atchFileIdntfNo);
        storedFile.setFileSn(atchFileDetailMapper.selectAtchFileDtlNextSn(atchFileIdntfNo));
        storedFile.setFileStrgPathNm(relativeDirectory.replace('\\', '/'));
        storedFile.setFileStrgNm(storedFileName);
        storedFile.setOrgnlFileNm(originalFileName);
        storedFile.setFileExtnNm(extension);
        storedFile.setFileCn(description);
        storedFile.setFileSz(multipartFile.getSize());
        atchFileDetailMapper.insertAtchFileDtl(storedFile);
        storedFile.setUrl(toPublicUrl(atchFileIdntfNo, storedFile.getFileSn()));
        return storedFile;
    }

    @Transactional(readOnly = true)
    public StoredFile getStoredFile(String atchFileIdntfNo, int fileSn) {
        StoredFile storedFile = atchFileDetailMapper.selectAtchFileDtl(atchFileIdntfNo, fileSn);
        if (storedFile != null) {
            storedFile.setUrl(toPublicUrl(atchFileIdntfNo, fileSn));
        }
        return storedFile;
    }

    @Transactional(readOnly = true)
    public Path resolvePath(StoredFile storedFile) {
        Path rootPath = Paths.get(properties.getFile().getUploadPath()).toAbsolutePath().normalize();
        Path targetPath = rootPath
            .resolve(storedFile.getFileStrgPathNm())
            .resolve(storedFile.getFileStrgNm())
            .normalize();

        if (!targetPath.startsWith(rootPath)) {
            throw new IllegalArgumentException("허용되지 않은 파일 경로입니다.");
        }
        return targetPath;
    }

    public String toPublicUrl(String atchFileIdntfNo, int fileSn) {
        return "/comn/file/view/" + atchFileIdntfNo + "/" + fileSn;
    }

    private String sanitizeDirectory(String directory) {
        String value = StringUtils.hasText(directory) ? directory : "common";
        value = value.replace('\\', '/').replaceAll("[^a-zA-Z0-9/_-]", "");
        value = value.replaceAll("/+", "/");
        value = value.replaceAll("^/+", "");
        return StringUtils.hasText(value) ? value : "common";
    }

    private String extractExtension(String fileName) {
        if (!StringUtils.hasText(fileName) || !fileName.contains(".")) {
            return "";
        }
        return fileName.substring(fileName.lastIndexOf('.') + 1);
    }
}
