package tech.easyflow.common.filestorage;

import org.junit.Test;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;

/**
 * {@link FileStorageManager} 可恢复操作固定后端路由测试。
 */
public class FileStorageManagerTest {

    /**
     * 验证 prepare 使用当前后端，而后续操作在默认后端切换后仍按句柄后端路由。
     */
    @Test
    public void recoverableOperationsRouteByPreparedBackendAfterSwitch() throws IOException {
        RecordingStorage local = new RecordingStorage("local");
        RecordingStorage xFile = new RecordingStorage("xFileStorage");
        AtomicReference<String> current = new AtomicReference<>("local");
        FileStorageManager manager = new FileStorageManager(
                current::get, backend -> Map.of("local", local, "xFileStorage", xFile).get(backend));

        FileStorageWriteHandle handle = manager.prepareRecoverableWrite("skill-content/ab", "content.bin");
        current.set("xFileStorage");
        FileStorageWriteResult result = manager.saveRecoverable(null, handle);
        InputStream inputStream = manager.readRecoverable(handle);
        manager.deleteRecoverable(handle);
        boolean exists = manager.existsRecoverable(handle);

        assertEquals("local", handle.getBackend());
        assertSame(local.result, result);
        assertSame(local.recoverableInput, inputStream);
        assertEquals(1, local.prepareCalls);
        assertEquals(1, local.saveCalls);
        assertEquals(1, local.readCalls);
        assertEquals(1, local.deleteCalls);
        assertEquals(1, local.existsCalls);
        assertEquals(0, xFile.prepareCalls + xFile.saveCalls + xFile.readCalls
                + xFile.deleteCalls + xFile.existsCalls);
        assertFalse(exists);
    }

    /**
     * 可记录可恢复调用的存储测试替身。
     */
    private static final class RecordingStorage implements FileStorageService {
        /** 后端名称。 */
        private final String backend;
        /** 固定结果。 */
        private final FileStorageWriteResult result;
        /** 固定可恢复读取流。 */
        private final InputStream recoverableInput = InputStream.nullInputStream();
        /** prepare 调用次数。 */
        private int prepareCalls;
        /** save 调用次数。 */
        private int saveCalls;
        /** read 调用次数。 */
        private int readCalls;
        /** delete 调用次数。 */
        private int deleteCalls;
        /** exists 调用次数。 */
        private int existsCalls;

        /**
         * 创建指定名称的存储替身。
         *
         * @param backend 后端名称
         */
        private RecordingStorage(String backend) {
            this.backend = backend;
            FileStorageWriteHandle handle = new FileStorageWriteHandle(
                    backend, "", "/tmp/easyflow", "skill-content", "content.bin");
            this.result = new FileStorageWriteResult("/files/content.bin", handle.encodeLocator());
        }

        /** {@inheritDoc} */
        @Override public String save(MultipartFile file) { return ""; }
        /** {@inheritDoc} */
        @Override public void delete(String path) { }
        /** {@inheritDoc} */
        @Override public InputStream readStream(String path) throws IOException { return InputStream.nullInputStream(); }
        /** {@inheritDoc} */
        @Override public long getFileSize(String path) { return 0; }
        /** {@inheritDoc} */
        @Override public String save(File file, String prePath) { return ""; }

        /** {@inheritDoc} */
        @Override
        public FileStorageWriteHandle prepareRecoverableWrite(String path, String filename) {
            prepareCalls++;
            return new FileStorageWriteHandle(backend, "", "/tmp/easyflow", path, filename);
        }

        /** {@inheritDoc} */
        @Override
        public FileStorageWriteResult saveRecoverable(MultipartFile file, FileStorageWriteHandle handle) {
            saveCalls++;
            return result;
        }

        /** {@inheritDoc} */
        @Override
        public InputStream readRecoverable(FileStorageWriteHandle handle) {
            readCalls++;
            return recoverableInput;
        }

        /** {@inheritDoc} */
        @Override
        public void deleteRecoverable(FileStorageWriteHandle handle) {
            deleteCalls++;
        }

        /** {@inheritDoc} */
        @Override
        public boolean existsRecoverable(FileStorageWriteHandle handle) {
            existsCalls++;
            return false;
        }
    }
}
