const std = @import("std");
const httpz = @import("../.deps/http.zig/src/httpz.zig");
const models = @import("../db/models.zig");
const ztime = @import("../.deps/time.zig");
const utils = @import("../utils.zig");
const auth = @import("auth.zig");
const handler = @import("../http_handler.zig");

// pub fn getBudget(req: *httpz.Request, res: *httpz.Response) !void {
//     const db = handler.getDb();

//     var gpa = std.heap.GeneralPurposeAllocator(.{}){};
//     const allocator = gpa.allocator();
//     const id_str = req.param("id");
//     if (id_str == null) {
//         res.status = 400;
//         res.body = "Bad Request: No Id";
//         return;
//     }
//     const id = std.fmt.parseInt(u32, id_str.?, 0) catch {
//         res.status = 401;
//         res.body = "Bad Request: Bad Id";
//         return;
//     };

//     const budget = try db.selectOneById(models.Budget, allocator, id);

//     if (budget == null) {
//         res.status = 404;
//         res.body = "Budget not found";
//         return;
//     }

//     try res.json(budget.?, .{});
// }

// const BudgetPostReq = struct {
//     id: ?u32,
//     family_id: u32,
//     name: []const u8,
//     created_at: ?u64,
//     updated_at: ?u64,
//     hide: u8,
// };

// pub fn putBudget(req: *httpz.Request, res: *httpz.Response) !void {
//     var db = handler.getDb();
//     var gpa = std.heap.GeneralPurposeAllocator(.{}){};
//     const allocator = gpa.allocator();

//     const body_data = req.json(models.Budget) catch |err| {
//         std.debug.print("Malformed body: {any}\n", .{err});
//         handler.returnError("Bad Request: Malformed Body", 400, res);
//         return;
//     };
//     if (body_data == null) {
//         handler.returnError("Bad Request: No Data", 400, res);
//         return;
//     }
//     var body = body_data.?;

//     // Add Budget
//     const now = @intCast(u64, std.time.milliTimestamp());
//     // Update existing Budget
//     body.updated_at = now;
//     try db.updateById(models.Budget, body);

//     const query = models.createSelectOnIdQuery(models.Transaction);
//     const updated_budget = try db.selectOne(models.Budget, allocator, query, .{ .id = body.id });
//     if (updated_budget) |budget| {
//         try handler.returnData(budget, res);
//     } else {
//         handler.returnError("Internal Server Error", 500, res);
//     }
//     return;
// }

// pub fn postBudget(req: *httpz.Request, res: *httpz.Response) !void {
//     comptime {
//         const putReqLen = @typeInfo(BudgetPostReq).Struct.fields.len;
//         const budgetLen = @typeInfo(models.Budget).Struct.fields.len;
//         if (putReqLen != budgetLen) {
//             @compileError(std.fmt.comptimePrint("BudgetPostReq does not equal Budget model struct, fields inconsistent", .{}));
//         }
//     }

//     var db = handler.getDb();
//     var gpa = std.heap.GeneralPurposeAllocator(.{}){};
//     const allocator = gpa.allocator();

//     const body_data = req.json(BudgetPostReq) catch |err| {
//         std.debug.print("Malformed body: {any}\n", .{err});
//         handler.returnError("Bad request: Malformed Body", 400, res);
//         return;
//     };
//     if (body_data == null) {
//         handler.returnError("Bad request: No Data", 400, res);
//         return;
//     }
//     var body = body_data.?;

//     if (body.id != null) {
//         handler.returnError("Bad Request: ID", 400, res);
//     }
//     // Add Budget
//     const now = @intCast(u64, std.time.milliTimestamp());
//     // Create Budget
//     body.created_at = now;
//     body.updated_at = now;

//     try db.insert(models.Budget, utils.removeStructFields(body, &[_]u8{0}));

//     // Get Budget
//     const query = try models.createSelectOnFieldQuery(models.Budget, null, "created_at", "=");
//     const updated_budget = try db.selectOne(models.Budget, allocator, query, .{ .created_at = body.created_at });
//     if (updated_budget) |budget| {
//         try handler.returnData(budget, res);
//     } else {
//         handler.returnError("Internal Server Error", 500, res);
//     }
//     return;
// }

const BudgetCatPostReq = struct {
    id: ?u32,
    budget_id: u32,
    amount: f64,
    name: []const u8,
    color: []const u8,
    created_at: ?u64,
    updated_at: ?u64,
    hide: u8,
};

pub fn postBudgetCategory(req: *httpz.Request, res: *httpz.Response) !void {
    comptime {
        const putReqLen = @typeInfo(BudgetCatPostReq).Struct.fields.len;
        const budgetLen = @typeInfo(models.BudgetCategory).Struct.fields.len;
        if (putReqLen != budgetLen) {
            @compileError(std.fmt.comptimePrint("BudgetCatPutReq does not equal Budget model struct, fields inconsistent", .{}));
        }
    }

    var db = handler.getDb();
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    const body_data = req.json(BudgetCatPostReq) catch |err| {
        std.debug.print("Malformed body: {any}\n", .{err});

        handler.returnError("Bad request: Malformed Body", 400, res);
        return;
    };
    if (body_data == null) {
        handler.returnError("Bad request: No Data", 400, res);
        return;
    }
    var body = body_data.?;

    if (body.id != null) {
        handler.returnError("Bad request: ID", 400, res);
        return;
    }

    const budget = try db.selectOneById(models.Budget, allocator, body.budget_id);
    if (budget == null) {
        handler.returnError("No budget found", 404, res);
    }

    _ = auth.verifyRequest(req, res, null, budget.?.family_id) catch {
        return;
    };

    // Add Budget
    const now = @intCast(u64, std.time.milliTimestamp());
    // Create Budget
    body.created_at = now;
    body.updated_at = now;

    try db.insert(models.BudgetCategory, utils.removeStructFields(body, &[_]u8{0}));

    // Get Budget
    const query = try models.createSelectOnFieldQuery(models.BudgetCategory, null, "created_at", "=");
    const updated_budget_cat = try db.selectOne(models.BudgetCategory, allocator, query, .{ .created_at = body.created_at });

    if (updated_budget_cat == null) {
        std.debug.print("Could not find inserted budget", .{});
        handler.returnError("Internal Server Error", 500, res);
        return;
    }
    try handler.returnData(updated_budget_cat.?, res);
}

pub fn putBudgetCategory(req: *httpz.Request, res: *httpz.Response) !void {
    var db = handler.getDb();
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    const body_data = req.json(models.BudgetCategory) catch |err| {
        std.debug.print("Malformed body: {any}\n", .{err});
        handler.returnError("Bad request: Malformed Body", 400, res);
        return;
    };
    if (body_data == null) {
        handler.returnError("Bad request: No Data", 400, res);
        return;
    }
    var budget_category = body_data.?;

    const budget = try db.selectOneById(models.Budget, allocator, budget_category.budget_id);
    if (budget == null) {
        handler.returnError("No budget found", 404, res);
    }

    _ = auth.verifyRequest(req, res, null, budget.?.family_id) catch {
        return;
    };

    const now = @intCast(u64, std.time.milliTimestamp());

    // Update existing Budget
    budget_category.updated_at = now;
    try db.updateById(models.BudgetCategory, budget_category);

    const query = models.createSelectOnIdQuery(models.BudgetCategory);
    const updated_budget_cat = try db.selectOne(models.BudgetCategory, allocator, query, .{ .id = budget_category.id });
    if (updated_budget_cat == null) {
        std.debug.print("Could not find inserted budget", .{});
        handler.returnError("Internal Server Error", 500, res);
        return;
    }
    try handler.returnData(updated_budget_cat.?, res);
    return;
}

const deleteIdReq = struct {
    id: u32,
};

pub fn deleteBudgetCategory(req: *httpz.Request, res: *httpz.Response) !void {
    var db = handler.getDb();
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    const body = handler.getReqJson(req, res, deleteIdReq) catch {
        return;
    };

    const budget_category = try db.selectOneById(models.BudgetCategory, allocator, body.id);
    if (budget_category == null) {
        return handler.returnError("Cannot find budget", 404, res);
    }
    const budget = try db.selectOneById(models.Budget, allocator, budget_category.?.budget_id);
    if (budget == null) {
        return handler.returnError("Cannot find budget", 404, res);
    }

    _ = auth.verifyRequest(req, res, null, budget.?.family_id) catch {
        return;
    };

    try db.updateHideById(models.BudgetCategory, true, body.id);

    const updated_budget_category = try db.selectOneById(models.BudgetCategory, allocator, body.id);
    if (budget_category == null) {
        return handler.returnError("Could not delete category", 500, res);
    }
    return try handler.returnData(updated_budget_category.?, res);
}