import moment from "moment";
import { v4 as uuidv4 } from "uuid";
import { Op, fn, col, literal } from "sequelize";
import { User } from "@/models/user";
import { Patient } from "@/models/patient";
import { z } from "zod";

export const CreatePatient = async (req, res, next) => {
    try {
        const validator = z.object({
            fullname: z
                .string({ required_error: "missing required field: fullname" })
                .min("fullname must be provided"),
            birth_date: z
                .string({
                    required_error: "missing required field: birth_date",
                })
                .min("birth_date must be provided"),
            gender: z.enum(["male", "female"], {
                required_error: "missing required field: gender",
            }),
        });

        const body = validator.parse(req.body);
        const patient = (
            await Patient.create({
                ...body,
                id: uuidv4(),
                created_by_id: req.user.id,
            })
        )?.get({ plain: true });

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

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

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

        const patients = await Patient.findAll({
            where: query,
            include: [
                {
                    model: User,
                    as: "created_by",
                    attributes: ["id", "username", "email", "role"],
                },
            ],
            order: [["created_at", "DESC"]],
            limit: Number(limit),
            offset: (Number(page) > 1 ? Number(page) - 1 : 0) * Number(limit),
        });

        const data_size = await Patient.count(q);
        const page_size = Math.ceil(data_size / Number(limit));
        const newest = await Patient.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: patients,
        });
    } catch (err) {
        next(err);
    }
};

export const GetPatientById = async (req, res, next) => {
    try {
        const patient = await Patient.findByPk(req.params.id);

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

export const GetPatientGrowthChart = async (req, res, next) => {
    try {
        const stats = await Patient.findAll({
            attributes: [
                [
                    fn(
                        "DATE",
                        fn("CONVERT_TZ", col("created_at"), "+00:00", "+07:00"),
                    ),
                    "date",
                ],
                [fn("COUNT", "*"), "size"],
            ],
            where: {
                created_at: {
                    [Op.gte]: moment()
                        .subtract(29, "days")
                        .startOf("day")
                        .toDate(),
                },
            },
            group: [
                fn(
                    "DATE",
                    fn("CONVERT_TZ", col("created_at"), "+00:00", "+07:00"),
                ),
            ],
            order: [
                [
                    fn(
                        "DATE",
                        fn("CONVERT_TZ", col("created_at"), "+00:00", "+07:00"),
                    ),
                    "ASC",
                ],
            ],
        });

        const periodeACount = await Patient.count({
            where: {
                created_at: {
                    [Op.between]: [
                        moment().subtract(29, "days").startOf("day").toDate(),
                        moment().endOf("day").toDate(),
                    ],
                },
            },
        });

        const periodeBCount = await Patient.count({
            where: {
                created_at: {
                    [Op.between]: [
                        moment().subtract(59, "days").startOf("day").toDate(),
                        moment().subtract(30, "days").endOf("day").toDate(),
                    ],
                },
            },
        });

        let percentage = 0;

        if (periodeBCount === 0) {
            percentage = periodeACount > 0 ? 100 : 0;
        } else {
            percentage =
                ((periodeACount - periodeBCount) / periodeBCount) * 100;
        }

        res.status(200).json({
            success: true,
            data: {
                size: periodeACount,
                percentage: parseFloat(percentage.toFixed(2)),
                stats,
            },
        });
    } catch (err) {
        next(err);
    }
};
