package tech.easyflow.publicapi.service;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;

/**
 * {@link WorkflowApiMultipartParameterMapper} 参数映射测试。
 */
public class WorkflowApiMultipartParameterMapperTest {

    private final WorkflowApiMultipartParameterMapper mapper =
            new WorkflowApiMultipartParameterMapper();

    /**
     * 验证 API metadata 与同名工作流文件参数互不冲突。
     */
    @Test
    public void mapShouldSeparateApiMetadataAndWorkflowParameter() {
        MultiValueMap<String, MultipartFile> parts =
                new LinkedMultiValueMap<>();
        parts.add(
                "metadata",
                file("metadata", "metadata.json", "{}"));
        parts.add(
                "files.metadata",
                file("files.metadata", "one.pdf", "one"));
        parts.add(
                "files.metadata",
                file("files.metadata", "two.pdf", "two"));

        Map<String, List<MultipartFile>> mapped = mapper.map(parts);

        Assert.assertEquals(1, mapped.size());
        Assert.assertEquals(2, mapped.get("metadata").size());
        Assert.assertEquals(
                "one.pdf",
                mapped.get("metadata").get(0).getOriginalFilename());
    }

    /**
     * 验证未使用文件命名空间的 Part 会被明确拒绝。
     */
    @Test
    public void mapShouldRejectUnnamespacedFilePart() {
        MultiValueMap<String, MultipartFile> parts =
                new LinkedMultiValueMap<>();
        parts.add(
                "documents",
                file("documents", "one.pdf", "one"));

        BusinessException exception = Assert.assertThrows(
                BusinessException.class,
                () -> mapper.map(parts));

        Assert.assertTrue(
                exception.getMessage().contains("files.<"));
    }

    /**
     * 验证 Part 参数名不会被隐式 trim 后误映射到另一个工作流参数。
     */
    @Test
    public void mapShouldRejectWhitespaceAlteredParameterName() {
        MultiValueMap<String, MultipartFile> parts =
                new LinkedMultiValueMap<>();
        parts.add(
                "files. metadata",
                file("files. metadata", "one.pdf", "one"));

        BusinessException exception = Assert.assertThrows(
                BusinessException.class,
                () -> mapper.map(parts));

        Assert.assertTrue(
                exception.getMessage().contains("参数名无效"));
    }

    /**
     * 创建测试文件 Part。
     *
     * @param partName Part 名
     * @param filename 文件名
     * @param content 文件内容
     * @return 测试文件
     */
    private MultipartFile file(
            String partName,
            String filename,
            String content) {
        return new TestMultipartFile(
                partName,
                filename,
                "application/octet-stream",
                content.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 无需 Spring Test 依赖的最小 MultipartFile 测试实现。
     */
    private record TestMultipartFile(
            String name,
            String originalFilename,
            String contentType,
            byte[] bytes) implements MultipartFile {

        /**
         * 获取 Part 名。
         *
         * @return Part 名
         */
        @Override
        public String getName() {
            return name;
        }

        /**
         * 获取原始文件名。
         *
         * @return 原始文件名
         */
        @Override
        public String getOriginalFilename() {
            return originalFilename;
        }

        /**
         * 获取内容类型。
         *
         * @return 内容类型
         */
        @Override
        public String getContentType() {
            return contentType;
        }

        /**
         * 判断文件是否为空。
         *
         * @return 是否为空
         */
        @Override
        public boolean isEmpty() {
            return bytes.length == 0;
        }

        /**
         * 获取文件字节数。
         *
         * @return 文件字节数
         */
        @Override
        public long getSize() {
            return bytes.length;
        }

        /**
         * 获取文件内容。
         *
         * @return 文件内容副本
         */
        @Override
        public byte[] getBytes() {
            return bytes.clone();
        }

        /**
         * 打开文件内容流。
         *
         * @return 文件内容流
         */
        @Override
        public InputStream getInputStream() {
            return new ByteArrayInputStream(bytes);
        }

        /**
         * 将测试文件写入目标文件。
         *
         * @param destination 目标文件
         * @throws IOException 写入失败
         */
        @Override
        public void transferTo(File destination) throws IOException {
            Files.write(destination.toPath(), bytes);
        }
    }
}
