import { prisma } from "../constants";
import { TimeEntryController } from "../controllers/TimeEntryController";

const timeEntryIncludes = {
    status: true,
    chargeCode: true,
    company: true,
    serviceTicket: true,
    activity: true,
    contact: true,
    location: true,
} as const;

export const timeEntries = {
    async fetch(identifier: string | number): Promise<TimeEntryController> {
        const isNumeric =
            typeof identifier === "number" || /^\d+$/.test(String(identifier));

        const entry = await prisma.timeEntry.findFirst({
            where: isNumeric
                ? { id: Number(identifier) }
                : { uid: String(identifier) },
            include: timeEntryIncludes,
        });

        if (!entry) throw new Error("Unknown time entry.");

        return new TimeEntryController(entry);
    },

    async count(): Promise<number> {
        return prisma.timeEntry.count();
    },

    async fetchPages(page: number, rpp: number): Promise<TimeEntryController[]> {
        page = page.valueOf();
        rpp = rpp.valueOf();

        const skip = (page > 1 ? page - 1 : 0) * rpp;
        const take = rpp ?? 30;

        const data = await prisma.timeEntry.findMany({
            skip,
            take,
            include: timeEntryIncludes,
            orderBy: { dateStart: "desc" },
        });

        return data.map((e) => new TimeEntryController(e));
    },

    async search(
        query: string,
        page: number,
        rpp: number
    ): Promise<TimeEntryController[]> {
        page = page.valueOf();
        rpp = rpp.valueOf();

        const skip = (page > 1 ? page - 1 : 0) * rpp;
        const take = rpp ?? 30;

        const numericQuery = parseInt(query, 10);

        const data = await prisma.timeEntry.findMany({
            where: {
                OR: [
                    { notes: { contains: query, mode: "insensitive" } },
                    { internalNote: { contains: query, mode: "insensitive" } },
                    { uid: { contains: query, mode: "insensitive" } },
                    ...(!isNaN(numericQuery) ? [{ id: numericQuery }] : []),
                ],
            },
            skip,
            take,
            include: timeEntryIncludes,
            orderBy: { dateStart: "desc" },
        });

        return data.map((e) => new TimeEntryController(e));
    },

    async fetchByMember(
        memberId: string,
        page: number,
        rpp: number
    ): Promise<TimeEntryController[]> {
        page = page.valueOf();
        rpp = rpp.valueOf();

        const skip = (page > 1 ? page - 1 : 0) * rpp;
        const take = rpp ?? 30;

        const data = await prisma.timeEntry.findMany({
            where: { memberId },
            skip,
            take,
            include: timeEntryIncludes,
            orderBy: { dateStart: "desc" },
        });

        return data.map((e) => new TimeEntryController(e));
    },

    async countByMember(memberId: string): Promise<number> {
        return prisma.timeEntry.count({ where: { memberId } });
    },

    async fetchByTicket(
        serviceTicketId: number
    ): Promise<TimeEntryController[]> {
        const data = await prisma.timeEntry.findMany({
            where: { serviceTicketId },
            include: timeEntryIncludes,
            orderBy: { dateStart: "desc" },
        });

        return data.map((e) => new TimeEntryController(e));
    },

    async fetchByActivity(activityId: number): Promise<TimeEntryController[]> {
        const data = await prisma.timeEntry.findMany({
            where: { activityId },
            include: timeEntryIncludes,
            orderBy: { dateStart: "desc" },
        });

        return data.map((e) => new TimeEntryController(e));
    },

    async fetchByDateRange(
        startDate: Date,
        endDate: Date,
        memberId?: string
    ): Promise<TimeEntryController[]> {
        const data = await prisma.timeEntry.findMany({
            where: {
                dateStart: { gte: startDate, lte: endDate },
                ...(memberId ? { memberId } : {}),
            },
            include: timeEntryIncludes,
            orderBy: { dateStart: "asc" },
        });

        return data.map((e) => new TimeEntryController(e));
    },
};
