const test = require("node:test");
const assert = require("node:assert/strict");
const { once } = require("events");

const sharp = require("sharp");

const app = require("../src/server");

async function createTestImage() {
    const width = 200;
    const height = 200;

    const pixels = Buffer.alloc(
        width * height * 4
    );

    for (
        let i = 0;
        i < pixels.length;
        i += 4
    ) {
        pixels[i] = 128;
        pixels[i + 1] = 128;
        pixels[i + 2] = 128;
        pixels[i + 3] = 255;
    }

    return sharp(pixels, {
        raw: {
            width,
            height,
            channels: 4
        }
    })
        .png()
        .toBuffer();
}

async function createServer() {
    const server = app.listen(0);

    await once(server, "listening");

    const address = server.address();

    return {
        server,
        baseUrl: `http://127.0.0.1:${address.port}`
    };
}

test("GET /api/health", async () => {
    const {
        server,
        baseUrl
    } = await createServer();

    try {
        const response = await fetch(
            `${baseUrl}/api/health`
        );

        assert.equal(
            response.status,
            200
        );

        const body = await response.json();

        assert.deepEqual(body, {
            status: "ok"
        });
    } finally {
        server.close();
    }
});

test(
    "encode and decode a message through the API",
    async () => {
        const {
            server,
            baseUrl
        } = await createServer();

        try {
            const image =
                await createTestImage();

            const encodeForm =
                new FormData();

            encodeForm.append(
                "image",
                new Blob(
                    [image],
                    {
                        type: "image/png"
                    }
                ),
                "test.png"
            );

            encodeForm.append(
                "message",
                "Hello from the integration test!"
            );

            encodeForm.append(
                "password",
                "test-password"
            );

            const encodedResponse =
                await fetch(
                    `${baseUrl}/api/encode`,
                    {
                        method: "POST",
                        body: encodeForm
                    }
                );

            assert.equal(
                encodedResponse.status,
                200
            );

            assert.equal(
                encodedResponse.headers.get(
                    "content-type"
                ),
                "image/png"
            );

            const encodedImage =
                Buffer.from(
                    await encodedResponse.arrayBuffer()
                );

            assert.ok(
                encodedImage.length > 0
            );

            const decodeForm =
                new FormData();

            decodeForm.append(
                "image",
                new Blob(
                    [encodedImage],
                    {
                        type: "image/png"
                    }
                ),
                "encoded.png"
            );

            decodeForm.append(
                "password",
                "test-password"
            );

            const decodedResponse =
                await fetch(
                    `${baseUrl}/api/decode`,
                    {
                        method: "POST",
                        body: decodeForm
                    }
                );

            assert.equal(
                decodedResponse.status,
                200
            );

            const result =
                await decodedResponse.json();

            assert.deepEqual(
                result,
                {
                    type: "message",
                    message:
                        "Hello from the integration test!"
                }
            );
        } finally {
            server.close();
        }
    }
);

test(
    "encode and decode a file through the API",
    async () => {
        const {
            server,
            baseUrl
        } = await createServer();

        try {
            const image =
                await createTestImage();

            const originalFile =
                Buffer.from(
                    "This is a secret file.\n"
                );

            const encodeForm =
                new FormData();

            encodeForm.append(
                "image",
                new Blob(
                    [image],
                    {
                        type: "image/png"
                    }
                ),
                "test.png"
            );

            encodeForm.append(
                "file",
                new Blob(
                    [originalFile],
                    {
                        type: "text/plain"
                    }
                ),
                "secret.txt"
            );

            encodeForm.append(
                "password",
                "test-password"
            );

            const encodedResponse =
                await fetch(
                    `${baseUrl}/api/encode`,
                    {
                        method: "POST",
                        body: encodeForm
                    }
                );

            assert.equal(
                encodedResponse.status,
                200
            );

            assert.equal(
                encodedResponse.headers.get(
                    "content-type"
                ),
                "image/png"
            );

            const encodedImage =
                Buffer.from(
                    await encodedResponse.arrayBuffer()
                );

            assert.ok(
                encodedImage.length > 0
            );

            const decodeForm =
                new FormData();

            decodeForm.append(
                "image",
                new Blob(
                    [encodedImage],
                    {
                        type: "image/png"
                    }
                ),
                "encoded.png"
            );

            decodeForm.append(
                "password",
                "test-password"
            );

            const decodedResponse =
                await fetch(
                    `${baseUrl}/api/decode`,
                    {
                        method: "POST",
                        body: decodeForm
                    }
                );

            assert.equal(
                decodedResponse.status,
                200
            );

            const decodedFile =
                Buffer.from(
                    await decodedResponse.arrayBuffer()
                );

            assert.deepEqual(
                decodedFile,
                originalFile
            );

            assert.equal(
                decodedResponse.headers.get(
                    "content-type"
                ),
                "text/plain"
            );

            assert.match(
                decodedResponse.headers.get(
                    "content-disposition"
                ),
                /secret\.txt/
            );
        } finally {
            server.close();
        }
    }
);