package tech.easyflow.agent.runtime.media;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;
import tech.easyflow.agent.config.AgentMediaProperties;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

/**
 * Agent 图片格式识别、规范化和资源限制测试。
 */
public class AgentImageProcessorTest {

    /**
     * 验证 PNG 会保留格式、尺寸和稳定摘要。
     *
     * @throws Exception 图片生成失败时抛出
     */
    @Test
    public void processShouldAcceptPngAndExposeMetadata() throws Exception {
        AgentImageProcessor processor = new AgentImageProcessor(new AgentMediaProperties());

        AgentImageData image = processor.process(file("sample.png", "image/png", imageBytes("png", 3, 2)));

        Assert.assertEquals("image/png", image.mimeType());
        Assert.assertEquals("png", image.extension());
        Assert.assertEquals(3, image.width());
        Assert.assertEquals(2, image.height());
        Assert.assertEquals(64, image.sha256().length());
    }

    /**
     * 验证 GIF 首帧会转换为模型兼容的 PNG。
     *
     * @throws Exception 图片生成失败时抛出
     */
    @Test
    public void processShouldNormalizeGifToPng() throws Exception {
        AgentImageProcessor processor = new AgentImageProcessor(new AgentMediaProperties());

        AgentImageData image = processor.process(file("sample.gif", "image/gif", imageBytes("gif", 4, 3)));

        Assert.assertEquals("image/png", image.mimeType());
        Assert.assertEquals("png", image.extension());
        Assert.assertEquals(4, image.width());
        Assert.assertEquals(3, image.height());
        Assert.assertArrayEquals(new byte[]{(byte) 0x89, 0x50, 0x4e, 0x47},
                java.util.Arrays.copyOf(image.bytes(), 4));
    }

    /**
     * 验证扩展名无法绕过真实图片内容校验。
     */
    @Test
    public void processShouldRejectUnsupportedContent() {
        AgentImageProcessor processor = new AgentImageProcessor(new AgentMediaProperties());

        ResponseStatusException error = Assert.assertThrows(ResponseStatusException.class,
                () -> processor.process(file("fake.png", "image/png", "not-an-image".getBytes())));

        Assert.assertEquals(400, error.getStatusCode().value());
    }

    /**
     * 验证像素上限会在图片解码前置阶段生效。
     *
     * @throws Exception 图片生成失败时抛出
     */
    @Test
    public void processShouldRejectImageOverPixelLimit() throws Exception {
        AgentMediaProperties properties = new AgentMediaProperties();
        properties.setMaxImagePixels(3);
        AgentImageProcessor processor = new AgentImageProcessor(properties);

        ResponseStatusException error = Assert.assertThrows(ResponseStatusException.class,
                () -> processor.process(file("large.png", "image/png", imageBytes("png", 2, 2))));

        Assert.assertEquals(400, error.getStatusCode().value());
        Assert.assertTrue(error.getReason().contains("像素过大"));
    }

    private byte[] imageBytes(String format, int width, int height) throws IOException {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        Assert.assertTrue(ImageIO.write(image, format, output));
        return output.toByteArray();
    }

    private MultipartFile file(String name, String contentType, byte[] bytes) {
        return new ByteArrayMultipartFile(name, contentType, bytes);
    }

    /**
     * 无磁盘依赖的测试 MultipartFile。
     */
    private static final class ByteArrayMultipartFile implements MultipartFile {
        private final String name;
        private final String contentType;
        private final byte[] bytes;

        private ByteArrayMultipartFile(String name, String contentType, byte[] bytes) {
            this.name = name;
            this.contentType = contentType;
            this.bytes = bytes;
        }

        @Override
        public String getName() { return "file"; }

        @Override
        public String getOriginalFilename() { return name; }

        @Override
        public String getContentType() { return contentType; }

        @Override
        public boolean isEmpty() { return bytes.length == 0; }

        @Override
        public long getSize() { return bytes.length; }

        @Override
        public byte[] getBytes() { return bytes.clone(); }

        @Override
        public InputStream getInputStream() { return new ByteArrayInputStream(bytes); }

        @Override
        public void transferTo(File destination) throws IOException {
            java.nio.file.Files.write(destination.toPath(), bytes);
        }
    }
}
