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

pub const WebRouter = struct {
const Self = @This();
allocator: std.mem.Allocator,

pub fn init(allocator: std.mem.Allocator) Self {
    return .{ .allocator = allocator };
}

pub fn index(self: *Self, req: zap.Request) void {
    const string = std.fmt.allocPrint(
    self.allocator,
    "Test",
    .{},
) catch return;
    defer self.allocator.free(string);
    req.sendFile("src/public/index.html") catch return;
}

pub fn home(self: *Self, req: zap.Request) void {
    const string = std.fmt.allocPrint(
    self.allocator,
    "HOME!!!",
    .{},
) catch return;
    defer self.allocator.free(string);
    req.sendBody(string) catch return;
}

pub fn blog(self: *Self, req: zap.Request) void {
    req.parseBody() catch |err| {
        std.log.err("parse error: {any}", .{err});
    };
    req.parseQuery();
    // looking for /blog?post=post_name
    if(req.getParamSlice("post")) |value| {
        // TODO: This will need to be updated to look at absolute
        // filepaths instead, it's a happy accident that this is safe now.
        const filepath = std.fmt.allocPrint(self.allocator, "./src/public/blog/{s}", .{value}) catch return;
        const dir = std.fs.cwd().openDir("./src/public/blog", .{ .iterate = true }) catch return;
        var walker = dir.walk(self.allocator) catch return;
        defer walker.deinit();
        // I believe that this is safe, since now the post_name now has to be
        // equal to one of the entries within the specified directory
        while (walker.next() catch return) |entry| {
            if(std.mem.eql(u8,entry.path,value)) {
                const file_content = std.fs.cwd().readFileAlloc(self.allocator, filepath, std.math.maxInt(usize)) catch return;
                defer self.allocator.free(file_content);
                req.sendBody(file_content) catch return;
            }
        }

        req.sendBody("ERROR: You shouldn't be looking here.") catch return;
    }
    req.sendBody("ERROR: Request for post is invalid.") catch return;
}
};

fn not_found(req: zap.Request) void {
    req.sendBody("404 - Not Found") catch return;
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{
        .thread_safe = true,
    }){};
    const allocator = gpa.allocator();
    var router = zap.Router.init(allocator, .{
        .not_found = not_found,
    });
    defer router.deinit();
    var web_router = WebRouter.init(allocator);
    try router.handle_func("/home", &web_router, &WebRouter.home);
    try router.handle_func("/index", &web_router, &WebRouter.index);
    try router.handle_func("/blog", &web_router, &WebRouter.blog);
    try router.handle_func("/", &web_router, &WebRouter.index);
    var listener = zap.HttpListener.init(.{ .port = 4000, .on_request = router.on_request_handler(), .log = true, .max_clients = 100000, .public_folder = "src/public" });
    try listener.listen();

    std.log.info("Listening on 0.0.0.0:4000\n", .{});

    zap.start(.{
        .threads = 2,
        .workers = 1, // 1 worker enables sharing state between threads
    });
}
