import bcrypt from "bcryptjs";
import { eq, sql } from "drizzle-orm";
import { Router } from "express";
import z from "zod";
import { db } from "../db/db.controller.js";
import {
	type NewScanUser,
	type ScanUser,
	scanUser,
} from "../db/schema/scanUsers.js";
import { requireAuth } from "../middleware/auth.middleware.js";
import { apiReturn, returnFunc } from "../utils/returnHelper.utils.js";
import { tryCatch } from "../utils/trycatch.utils.js";

const r = Router();

export async function hashPin(pin: string) {
	//   if (!/^\d{6}$/.test(pin)) {
	//     throw new Error("PIN must be exactly 6 digits");
	//   }

	return bcrypt.hashSync(pin, 12);
}

const registerSchema = z.object({
	name: z.string().min(2).max(100),
	pinNumber: z.string(),
	scannerId: z
		.string()
		.min(1)
		.max(500)
		.optional()
		.describe("if you leave blank it will be the same as your username"),
	role: z
		.enum(["user", "lead", "manager", "admin"])
		.optional()
		.describe("What roles are available to use."),
	pinHash: z.string().optional(),
});

r.post("/pin", async (req, res) => {
	const { pin } = req.body;

	if (!pin || pin.length !== 6) {
		return apiReturn(res, {
			success: false,
			level: "error",
			module: "mobile",
			subModule: "auth",
			message: `Pin number must be a min of 6 digits`,
			data: [],
			status: 401,
		});
	}

	// const user = await db
	// 	.select()
	// 	.from(scanUser)
	// 	.where(eq(scanUser.pinNumber, parseInt(pin, 10)));

	const user = await db.query.scanUser.findFirst({
		where: (u, { eq }) => eq(u.pinNumber, pin),
	});

	if (!user) {
		return apiReturn(res, {
			success: false,
			level: "error",
			module: "mobile",
			subModule: "auth",
			message: `Invalid login please try again.`,
			data: [],
			status: 401,
		});
	}

	const validPin = bcrypt.compareSync(pin, user.pinHash);

	if (!validPin) {
		return apiReturn(res, {
			success: false,
			level: "error",
			module: "mobile",
			subModule: "auth",
			message: `Invalid pin please try again.`,
			data: [],
			status: 401,
		});
	}

	return apiReturn(res, {
		success: true,
		level: "info",
		module: "mobile",
		subModule: "auth",
		message: `Welcome back ${user.name}`,
		data: user as ScanUser | any,
		status: 200,
	});
});

r.post("/user", async (req, res) => {
	try {
		// validate the body is correct before accepting it
		let validated = registerSchema.parse(req.body);

		validated = {
			...validated,
			pinHash: await hashPin(validated.pinNumber.toString()),
		};

		const values: NewScanUser = {
			name: validated.name,
			pinNumber: validated.pinNumber,
			pinHash: validated.pinHash ?? "",
			scannerId: validated.scannerId ?? "",
		};

		const newUser = await db.insert(scanUser).values(values).returning();

		apiReturn(res, {
			success: true,
			level: "info", //connect.success ? "info" : "error",
			module: "mobile",
			subModule: "auth",
			message: `${validated.name} was just created`,
			data: newUser as any,
			status: 200, //connect.success ? 200 : 400,
		});
	} catch (err) {
		if (err instanceof z.ZodError) {
			const flattened = z.flattenError(err);
			// return res.status(400).json({
			// 	error: "Validation failed",
			// 	details: flattened,
			// });

			return apiReturn(res, {
				success: false,
				level: "error", //connect.success ? "info" : "error",
				module: "mobile",
				subModule: "auth",
				message: "Validation failed",
				data: [flattened.fieldErrors],
				status: 400, //connect.success ? 200 : 400,
			});
		}

		return apiReturn(res, {
			success: false,
			level: "error", //connect.success ? "info" : "error",
			module: "mobile",
			subModule: "auth",
			message:
				"This User already exist with this pin or scanner id please try again",
			data: [err],
			status: 400, //connect.success ? 200 : 400,
		});
	}
});

r.get("/user", requireAuth, async (_, res) => {
	const { data, error } = await tryCatch(db.select().from(scanUser));

	// await trackLstEvent({
	// 	eventName: "mobile_get_users",
	// 	url: "/mobile/users",
	// 	eventData: {
	// 		module: "mobile",
	// 	},
	// });

	if (error) {
		return apiReturn(res, {
			success: false,
			level: "error",
			module: "mobile",
			subModule: "auth",
			message: `There was an error getting the user`,
			data: error as any,
			status: 400,
		});
	}

	if (!data) {
		return apiReturn(res, {
			success: true,
			level: "info",
			module: "mobile",
			subModule: "auth",
			message: `There are no users you should add one . `,
			data: [],
			status: 200,
		});
	}

	return apiReturn(res, {
		success: true,
		level: "info",
		module: "mobile",
		subModule: "auth",
		message: `All users. `,
		data,
		status: 200,
	});
});

r.patch("/user/:id", requireAuth, async (req, res) => {
	const updates: Record<string, unknown | null> = {};
	const { id } = req.params;

	const { data, error } = await tryCatch(
		db.query.scanUser.findFirst({
			where: (u, { eq }) => eq(u.id, `${id}`),
		}),
	);

	if (error) {
		return apiReturn(res, {
			success: false,
			level: "error",
			module: "mobile",
			subModule: "auth",
			message: `There was an error getting the user`,
			data: error as any,
			status: 400,
		});
	}

	if (!data) {
		return apiReturn(res, {
			success: false,
			level: "error",
			module: "mobile",
			subModule: "auth",
			message: `Invalid user id was passed over. `,
			data: [],
			status: 400,
		});
	}

	if (req.body?.name !== undefined) {
		updates.name = req.body.name;
	}

	if (req.body?.pinNumber !== undefined) {
		const existing = await db.query.scanUser.findFirst({
			where: (u, { eq }) => eq(u.pinHash, req.body.pinNumber),
		});

		if (existing)
			return returnFunc({
				success: false,
				level: "error",
				module: "mobile",
				subModule: "auth",
				message: `${req.body.pinNumber} already exists please try again`,
				data: [],
				notify: false,
				room: "",
			});
		updates.pinNumber = req.body.pinNumber;
		updates.pinHash = await hashPin(req.body.pinNumber);
	}

	if (req.body?.scannerId !== undefined) {
		updates.scannerId = req.body.scannerId;
	}

	if (req.body?.active !== undefined) {
		updates.active = req.body.active;
	}

	if (req.body?.excludedCommand !== undefined) {
		updates.excludedCommand = req.body.excludedCommand;
	}

	if (req.body?.role !== undefined) {
		updates.role = req.body.role;
	}

	updates.upd_date = sql`NOW()`;

	const updatedSetting = await db
		.update(scanUser)
		.set(updates)
		.where(eq(scanUser.id, `${id}`))
		.returning();

	return apiReturn(res, {
		success: true,
		level: "info",
		module: "mobile",
		subModule: "user",
		message: `User ${data.name} was updated. `,
		data: updatedSetting,
		status: 200,
	});
});

r.delete("/user/:id", requireAuth, async (req, res) => {
	const { id } = req.params;

	const { data, error } = await tryCatch(
		db.delete(scanUser).where(eq(scanUser.id, `${id}`)),
	);

	if (error) {
		return apiReturn(res, {
			success: false,
			level: "error",
			module: "mobile",
			subModule: "auth",
			message: `There was an error deleting the user`,
			data: error as any,
			status: 400,
		});
	}

	if (!data) {
		return apiReturn(res, {
			success: false,
			level: "error",
			module: "mobile",
			subModule: "auth",
			message: `There was no user to delete. `,
			data: [],
			status: 400,
		});
	}

	return apiReturn(res, {
		success: true,
		level: "info",
		module: "mobile",
		subModule: "user",
		message: `User was deleted. `,
		data: data ?? [],
		status: 200,
	});
});

export default r;
