pub fn Page(ctx: zx.PageContext) zx.Component {
    const allocator = ctx.arena;
    const formdata = ctx.request.multiFormData();

    const name = if (formdata.get("name")) |nd| nd.data else null;
    const picture = formdata.get("picture");
    const pic_file = if (picture) |pd| pd.filename else null;
    const pic_too_large = if (picture) |pd| pd.data.len > max_file_bytes else false;
    const pic_src = if (pic_too_large) null else formatImageSrc(ctx);

    return (
        <div @allocator={allocator}>
            <p>{name}</p>
            <p>{pic_file}</p>

            {if (pic_too_large) (<p>File too large. Max size is 1 MB.</p>)}
            {if (pic_src) |src| (<img src={src} height={100} width={100} />)}
            <form method="post" enctype="multipart/form-data">
                <input type="text" name="name" />
                <input type="file" name="picture" />
                <button type="submit">Submit</button>
            </form>
        </div>
    );
}

fn formatImageSrc(ctx: zx.PageContext) ?[]const u8 {
    const formdata = ctx.request.multiFormData();
    const picture = formdata.get("picture");
    const pic_data = if (picture) |p| p.data else null;
    const pic_file = if (picture) |p| p.filename else null;

    const pic_src = if (pic_data) |pd| b: {
        if (pd.len > max_file_bytes) break :b null;
        const mime = if (pic_file) |f| m: {
            if (std.ascii.eqlIgnoreCase(std.fs.path.extension(f), ".jpg") or std.ascii.eqlIgnoreCase(std.fs.path.extension(f), ".jpeg")) break :m "image/jpeg";
            if (std.ascii.eqlIgnoreCase(std.fs.path.extension(f), ".gif")) break :m "image/gif";
            if (std.ascii.eqlIgnoreCase(std.fs.path.extension(f), ".webp")) break :m "image/webp";
            if (std.ascii.eqlIgnoreCase(std.fs.path.extension(f), ".svg")) break :m "image/svg+xml";
            break :m "image/png";
        } else "image/png";

        const prefix = std.fmt.allocPrint(ctx.arena, "data:{s};base64,", .{mime}) catch break :b null;
        const encoded_len = std.base64.standard.Encoder.calcSize(pd.len);
        const out = ctx.arena.alloc(u8, prefix.len + encoded_len) catch break :b null;
        @memcpy(out[0..prefix.len], prefix);
        _ = std.base64.standard.Encoder.encode(out[prefix.len..], pd);
        break :b out;
    } else null;

    return pic_src;
}

pub const options = zx.PageOptions{
    .methods = &.{.POST},
};

const max_file_bytes: usize = 1_048_576 - (1024 * 10);

const zx = @import("zx");
const std = @import("std");
