import { PrismaClient, ProjectStatus } from '@prisma/client';

import {
    CustomerUpdateInputType,
    IdType,
    ProjectDbInsertType,
    ProjectStatusUpdateInputType,
    ProjectUpdateInputType,
} from '../../declarations/validations';
import { projectController } from '../../controller/projectController';

enum JobStatus {
    Created,
    PreProduction,
    InProduction,
    PostProduction,
    InRevision,
    WaitingForPayment,
    Closed,
    Cancelled,
}

export interface IProjectListService {}

export default class ProjectDatabaseService implements IProjectListService {
    private prisma = new PrismaClient();

    async insertProject(project: ProjectDbInsertType) {
        const { creatorId, customerId, ...projectProperties } = project;

        return this.prisma.project
            .create({
                data: {
                    ...projectProperties,
                    logs: {
                        create: {
                            profileId: creatorId,
                            status: ProjectStatus.Created,
                        },
                    },
                    participants: {
                        create: {
                            profileId: customerId,
                            roleId: 1, // TODO enum
                        },
                    },
                },
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async findProjectById(id: IdType) {
        return this.prisma.project
            .findUnique({
                where: {
                    id,
                },
                include: {
                    advisor: true,
                    videos: {
                        select: {
                            id: true,
                            version: true,
                            comment: true,
                        },
                    },
                    logs: true,
                    participants: {
                        include: {
                            profile: true,
                            role: true,
                        },
                    },
                },
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async findProjectByIdAndParticipation(id: IdType, participantId: IdType) {
        return this.prisma.project
            .findFirst({
                where: {
                    AND: [
                        {
                            id,
                        },
                        {
                            OR: [
                                {
                                    participants: {
                                        some: {
                                            profileId: participantId,
                                        },
                                    },
                                },
                                {
                                    advisorId: participantId,
                                },
                            ],
                        },
                    ],
                },
                include: {
                    advisor: true,
                    videos: {
                        select: {
                            id: true,
                            version: true,
                            comment: true,
                        },
                    },
                    logs: true,
                    participants: {
                        include: {
                            profile: true,
                            role: true,
                        },
                    },
                },
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async findAllProjects() {
        return (
            this.prisma.project
                .findMany({
                    orderBy: {
                        createdAt: 'desc',
                    },
                    include: {
                        participants: {
                            where: {
                                role: {
                                    name: 'customer', // TODO refactor to enum
                                },
                            },
                            select: {
                                profileId: true,
                                profile: {
                                    select: {
                                        firstname: true,
                                        lastname: true,
                                    },
                                },
                                role: {
                                    select: {
                                        name: true,
                                    },
                                },
                            },
                        },
                        advisor: {
                            select: {
                                firstname: true,
                                lastname: true,
                            },
                        },
                    },
                })
                //.then((projects) =>
                //    projects.map((project) => {
                //        this.prisma.projectParticipant.findMany({
                //          where: {
                //            projectId: project.id,
                //            role: {
                //
                //            }
                //          }
                //        });
                //    })
                //)
                .catch((e) => {
                    throw e;
                })
                .finally(async () => {
                    await this.prisma.$disconnect();
                })
        );
    }

    async findAllProjectsByProfileId(id: IdType) {
        return this.prisma.project
            .findMany({
                where: {
                    OR: [
                        {
                            participants: {
                                some: {
                                    profileId: id,
                                },
                            },
                        },
                        {
                            advisorId: id,
                        },
                    ],
                },
                orderBy: {
                    createdAt: 'desc',
                },
                //include: {
                //    participants: {
                //        some: {
                //            role: 'customer',
                //        },
                //    },
                //},
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async updateProject(project: ProjectUpdateInputType) {
        const { id, ...updatedObject } = project;
        return this.prisma.project
            .update({
                where: {
                    id,
                },
                data: {
                    ...updatedObject,
                },
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async findCurrentProjectStatus(id: number) {
        return this.prisma.projectLog
            .findFirst({
                where: {
                    projectId: id,
                },
                orderBy: {
                    timestamp: 'desc',
                },
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async updateProjectStatus(newStatus: ProjectStatusUpdateInputType) {
        return this.prisma.projectLog
            .create({
                data: {
                    ...newStatus,
                },
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async deleteProject(projectId: number) {
        return this.prisma.projectLog
            .deleteMany({
                where: {
                    projectId,
                },
            })
            .then(() => {
                this.prisma.projectParticipant
                    .deleteMany({
                        where: {
                            projectId,
                        },
                    })
                    .then(() =>
                        this.prisma.project.delete({
                            where: {
                                id: projectId,
                            },
                        })
                    );
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async countProjectsThisYear() {
        const currentYear = new Date().getFullYear();
        const firstDay = new Date(currentYear, 0, 1);
        const lastDay = new Date(currentYear, 11, 31);

        return this.prisma.project
            .count({
                where: {
                    createdAt: {
                        gte: firstDay,
                        lte: lastDay,
                    },
                },
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async summarizeEarningsThisYear() {
        const currentYear = new Date().getFullYear();
        const firstDay = new Date(currentYear, 0, 1);
        const lastDay = new Date(currentYear, 11, 31);

        return this.prisma.project
            .aggregate({
                sum: {
                    price: true,
                },
                where: {
                    createdAt: {
                        gte: firstDay,
                        lte: lastDay,
                    },
                },
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async countAllCustomersThisYear() {
        //    const currentYear = new Date().getFullYear();
        //    const firstDay = new Date(currentYear, 0, 1);
        //    const lastDay = new Date(currentYear, 11, 31);
        //
        //    return this.prisma.projectParticipant
        //        .groupBy({
        //            by: ['profileId'],
        //            where: {
        //              roleId: 1, // TODO enum
        //                createdAt: {
        //                    gte: firstDay,
        //                    lte: lastDay,
        //                },
        //            },
        //        })
        //        .catch((e) => {
        //            throw e;
        //        })
        //        .finally(async () => {
        //            await this.prisma.$disconnect();
        //        });
    }
}
