package cn.reghao.oss.console.account.service.impl;

import cn.reghao.oss.console.account.db.repository.DiskFileRepository;
import cn.reghao.oss.console.account.model.po.DiskFile;
import cn.reghao.oss.console.account.service.FileService;
import cn.reghao.jutil.jdk.security.DigestUtil;
import cn.reghao.jutil.web.ServletUtil;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.UUID;

/**
 * @author reghao
 * @date 2024-01-19 21:29:24
 */
@Service
public class FileServiceImpl implements FileService {
    // 1MiB
    private final int bufSize = 1024*1024;
    private final String storeDir = "";
    private final DiskFileRepository diskFileRepository;

    public FileServiceImpl(DiskFileRepository diskFileRepository) {
        this.diskFileRepository = diskFileRepository;
    }

    @Override
    public void initLocalStore() throws IOException {
        File dir1 = new File(storeDir);
        if (!dir1.exists()) {
            //FileUtils.forceMkdir(dir1);
        }
    }

    @Override
    public void getFile(String objectName) throws IOException {
        DiskFile diskFile = diskFileRepository.findByObjectName(objectName);
        if (diskFile == null) {
            writeResponse(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        String absolutePath = diskFile.getAbsolutePath();
        String contentType = diskFile.getContentType();
        long size = diskFile.getSize();
        HttpServletResponse response = ServletUtil.getResponse();
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType(contentType);
        response.setContentLengthLong(size);

        OutputStream outputStream = response.getOutputStream();
        writeResponse(outputStream, absolutePath, 0, size);
    }

    private void writeResponse(int statusCode) throws IOException {
        HttpServletResponse response = ServletUtil.getResponse();
        response.setStatus(statusCode);
        OutputStream outputStream = response.getOutputStream();
        outputStream.flush();
        outputStream.close();
    }

    private void writeResponse(OutputStream outputStream, String absolutePath, long start, long end) throws IOException {
        RandomAccessFile raf = new RandomAccessFile(absolutePath, "r");
        raf.seek(start);

        long len = end-start+1;
        if (len < bufSize) {
            int len1 = (int) len;
            byte[] buf1 = new byte[len1];
            int readLen1 = raf.read(buf1, 0, len1);
            outputStream.write(buf1, 0, readLen1);
        } else {
            byte[] buf = new byte[bufSize];
            long totalRead = 0;
            int readLen;
            while ((readLen = raf.read(buf, 0, bufSize)) != -1) {
                outputStream.write(buf, 0, readLen);
                totalRead += readLen;

                long left = len - totalRead;
                if (left < bufSize) {
                    int left1 = (int) left;
                    byte[] buf1 = new byte[left1];
                    int readLen1 = raf.read(buf1, 0, left1);
                    outputStream.write(buf1, 0, readLen1);
                    break;
                }
            }
        }

        outputStream.flush();
        outputStream.close();
        raf.close();
    }

    @Override
    public String putFile(MultipartFile file) throws Exception {
        long size = file.getSize();
        String filename = file.getOriginalFilename();
        String suffix = getSuffix(filename);

        String objectId = UUID.randomUUID().toString().replace("-", "");;
        String objectName;
        if (suffix.isBlank()) {
            objectName = String.format("file/%s", objectId);
        } else {
            objectName = String.format("file/%s%s", objectId, suffix);
        }

        String contentId = UUID.randomUUID().toString().replace("-", "");
        File savedFile = saveFile(file.getInputStream(), contentId, suffix);
        String contentType = Files.probeContentType(Path.of(savedFile.getAbsolutePath()));
        String sha256sum = DigestUtil.sha256sum(savedFile.getAbsolutePath());

        List<DiskFile> diskFiles = diskFileRepository.findBySha256sum(sha256sum);
        DiskFile diskFile;
        if (!diskFiles.isEmpty()) {
            DiskFile existFile = diskFiles.get(0);
            diskFile = new DiskFile(objectName, objectId, existFile, filename);
            FileUtils.deleteQuietly(savedFile);
        } else {
            diskFile = new DiskFile(objectName, objectId, savedFile.getAbsolutePath(), sha256sum, filename, contentType, size);
        }

        diskFileRepository.save(diskFile);
        return "/" + objectName;
    }

    private File saveFile(InputStream inputStream, String contentId, String suffix) throws IOException {
        String absolutePath = String.format("%s/%s%s", storeDir, contentId, suffix);
        File file = new File(absolutePath);
        if (file.exists()) {
            throw new IOException(absolutePath + " exist");
        }

        Files.copy(inputStream, Path.of(absolutePath), StandardCopyOption.REPLACE_EXISTING);
        return file;
    }

    private String getSuffix(String filename) {
        if (filename == null) {
            return "";
        }

        int idx = filename.lastIndexOf(".");
        return idx == -1 ? "" : filename.substring(idx);
    }
}
