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

export interface IUserListService {
    deleteUser(userId: number): Promise<void>;
    findUserByUsername(
        username: string
    ): Promise<
        | ({ [key: string]: any } & {
              id: number;
              username: string;
              lastLogin: Date | null;
              roles: any;
              profileId: number;
          })
        | null
    >;
    findPasswordHashByUserId(
        id: number
    ): Promise<{ passwordHash: string } | null>;
    //insertUser(
    //    username: string,
    //    passwordHash: string,
    //    mail?: string,
    //    profileId?: number
    //): Promise<object>;

    openSession(
        userId: number,
        token: string,
        loginTimestamp: Date,
        expireAt?: Date
    ): Promise<object>;
    findSessionByToken(token: string): Promise<object | null>;
    closeSession(token: string): Promise<object>;
}

export default class UserDatabaseService implements IUserListService {
    private prisma = new PrismaClient();

    async deleteUser(userId: number): Promise<void> {
        return this.prisma.projectParticipant
            .deleteMany({
                where: {
                    profileId: userId,
                },
            })
            .then(() =>
                this.prisma.userSession
                    .deleteMany({
                        where: {
                            user: {
                                profileId: userId,
                            },
                        },
                    })
                    .then(() => {
                        this.prisma.user
                            .deleteMany({
                                // TODO make unique -> profileId
                                where: {
                                    profileId: userId,
                                },
                            })
                            .then(() => {
                                this.prisma.profile.delete({
                                    where: {
                                        id: userId,
                                    },
                                });
                            });
                    })
            )
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    //async insertUser(
    //    username: string,
    //    passwordHash: string,
    //    mail?: string,
    //    employeeId?: number,
    //    customerId?: number
    //): Promise<object> {
    //    return this.prisma.user
    //        .create({
    //            data: {
    //                username,
    //                passwordHash,
    //                mail,
    //                profileId: 1, // TODO add to
    //            },
    //        })
    //        .catch((e) => {
    //            throw e;
    //        })
    //        .finally(async () => {
    //            await this.prisma.$disconnect();
    //        });
    //}

    async findUserByUsername(
        username: string
    ): Promise<{
        id: number;
        username: string;
        lastLogin: Date | null;
        roles: any;
        profileId: any;
    } | null> {
        return this.prisma.user
            .findFirst({
                where: {
                    username,
                },
                select: {
                    id: true,
                    username: true,
                    lastLogin: true,
                    roles: true,
                    profileId: true,
                },
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async findPasswordHashByUserId(
        id: number
    ): Promise<{ passwordHash: string } | null> {
        return this.prisma.user
            .findFirst({
                where: {
                    id,
                },
                select: {
                    passwordHash: true,
                },
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async findSessionByToken(token: string): Promise<object | null> {
        return this.prisma.userSession
            .update({
                where: {
                    token,
                },
                data: {
                    updatedAt: new Date(),
                },
                select: {
                    user: {
                        select: {
                            id: true,
                            username: true,
                        },
                    },
                },
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async openSession(
        userId: number,
        token: string,
        loginTimestamp: Date,
        expireAt?: Date
    ): Promise<object> {
        return this.prisma.userSession
            .upsert({
                where: {
                    token,
                },
                update: {
                    user: {
                        update: {
                            lastLogin: loginTimestamp,
                        },
                    },
                },
                create: {
                    token,
                    userId,
                    updatedAt: new Date(),
                    expireAt,
                },
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }

    async closeSession(token: string): Promise<object> {
        // Workaround for
        // return prisma.user.update({
        //    where: {
        //        sessionId: id
        //    },
        //    data: {
        //        sessionId: null
        //    }
        // });
        return this.prisma.userSession
            .deleteMany({
                where: {
                    token,
                },
            })
            .catch((e) => {
                throw e;
            })
            .finally(async () => {
                await this.prisma.$disconnect();
            });
    }
}
