import bcrypt from "bcryptjs";
import moment from "moment";
import { Op } from "sequelize";
import { v4 as uuidv4 } from "uuid";
import { User } from "@/models/user";
import { z } from "zod";

export const CreateUser = async (req, res, next) => {
    try {
        const validator = z.object({
            email: z
                .string({ required_error: "missing required field: email" })
                .email("invalid email"),
            username: z
                .string({ required_error: "missing required field: username" })
                .min(1, "username must be provided"),
            password: z
                .string({ required_error: "missing required field: password" })
                .min(1, "password must be provided"),
            role: z.enum(["admin", "staff"], {
                required_error: "missing required field: role",
            }),
        });

        const body = validator.parse(req.body);
        const hashedPassword = await bcrypt.hash(
            body.password,
            await bcrypt.genSalt(10),
        );

        const user = await User.create({
            ...body,
            id: uuidv4(),
            password: hashedPassword,
        });

        res.status(200).json({
            success: true,
            data: user,
        });
    } catch (err) {
        next(err);
    }
};

export const GetUsers = async (req, res, next) => {
    try {
        const { page = 1, limit = 10, search = "" } = req.query;

        const q = {};
        const query = {
            [Op.or]: ["username", "email"].map((field) => {
                return {
                    [field]: {
                        [Op.like]: `%${search}%`,
                    },
                };
            }),
        };

        const users = await User.findAll({
            where: query,
            order: [["created_at", "DESC"]],
            limit: Number(limit),
            offset: (Number(page) > 1 ? Number(page) - 1 : 0) * Number(limit),
            raw: true,
        });

        const data_size = await User.count(q);
        const page_size = Math.ceil(data_size / Number(limit));
        const newest = await User.count({
            where: {
                created_at: {
                    [Op.gte]: moment().subtract(7, "d").toISOString(),
                },
            },
        });

        res.status(200).json({
            success: true,
            properties: {
                all: data_size,
                new: newest,
            },
            pagination: {
                current_page: Number(page),
                limit: Number(limit),
                data_size,
                page_size,
                next: Number(page) < page_size ? Number(page) + 1 : 0,
                prev: Number(page) > 1 ? Number(page) - 1 : 0,
            },
            data: users,
        });
    } catch (err) {
        next(err);
    }
};

export const GetUserById = async (req, res, next) => {
    try {
        const user =
            req.params.id === "master"
                ? req.user
                : (await User.findByPk(req.params.id))?.get({ plain: true });

        delete user["password"];

        res.status(200).json({
            success: true,
            data: user,
        });
    } catch (err) {
        next(err);
    }
};

export const UpdateUser = async (req, res, next) => {
    try {
        const { password } = req.body;

        if (password) {
            req.body.password = await bcrypt.hash(
                password,
                await bcrypt.genSalt(10),
            );
        } else {
            delete req.body["password"];
        }

        await User.update({ ...req.body }, { where: { id: req.params.id } });
        const user = await User.findByPk(req.params.id);

        res.status(200).json({
            success: true,
            data: user,
        });
    } catch (err) {
        next(err);
    }
};

export const DeleteUser = async (req, res, next) => {
    try {
        const user = await User.findByPk(req.params.id);
        await User.destroy({ where: { id: req.params.id } });

        res.status(200).json({
            success: true,
            data: user,
        });
    } catch (err) {
        next(err);
    }
};
