import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
import { Prisma, PrismaClient } from '@prisma/client';

@Injectable()
export class DatabaseService extends PrismaClient implements OnModuleInit {
  public async onModuleInit() {
    await this.$connect();
  }

  public async enableShutdownHooks(app: INestApplication) {
    this.$on('beforeExit', async () => {
      await app.close();
    });
  }

  /**
   * Function to call the Database procedure
   * `Assign_Club`.
   *
   * @TODO Remove use of database procedure and use
   * Prisma-only code.
   *
   * @param ownerId The ID of the club owner
   * @param clubName The name of the club
   * @param statementType The statement type to send to
   *  the database procedure.
   * @returns Promise<unknown>
   */
  public async assignClub(
    ownerId: string,
    phoneId: number,
    clubName: string,
    statementType: string
  ): Promise<string | undefined> {
    // return this.$queryRaw(
    //   Prisma.sql`CALL Assign_Club(${ownerId}, ${clubName}, ${statementType})`
    // );

    if (statementType.toLowerCase().includes('insert')) {
      const { id } = await this.club.create({
        data: {
          owner: {
            connect: {
              id: ownerId
            }
          },
          phoneId,
          clubName
        }
      });

      return id;
    } else if (statementType.toLowerCase().includes('update')) {
      const club = await this.club.findFirst({
        where: {
          owner: {
            id: ownerId
          },
          phoneId
        }
      });

      if (club) {
        const { id } = await this.club.update({
          where: {
            id: club.id
          },
          data: {
            clubName
          }
        });

        return id;
      }
    } else {
      throw new Error(`Unhandled statement type: ${statementType}`);
    }

    return undefined;
  }

  /**
   * Function to call the Database procedure
   * `Create_Player`.
   *
   * @TODO Remove use of database procedure and use
   * Prisma-only code.
   *
   * @param email
   * @param phoneNumber
   * @param firstName
   * @param lastName
   * @param dateOfBirth
   * @param clubId
   * @returns Promise<unknown>
   */
  public async createPlayer(
    email: string,
    phoneNumber: string,
    firstName: string,
    lastName: string,
    dateOfBirth: Date,
    clubId: string
  ): Promise<unknown> {
    return this.$queryRaw(
      Prisma.sql`CALL Create_Player(${email}, ${phoneNumber}, ${firstName}, ${lastName}, ${dateOfBirth}, ${clubId})`
    );
  }

  /**
   * Function to call the Database procedure
   * `Create_PlayerV2`.
   *
   * @TODO Remove use of database procedure and use
   * Prisma-only code.
   *
   * @param email
   * @param phoneNumber
   * @param firstName
   * @param lastName
   * @param dateOfBirth
   * @param clubId
   * @returns Promise<unknown>
   */
  public async createPlayerV2(
    email: string,
    phoneNumber: string,
    firstName: string,
    lastName: string,
    dateOfBirth: Date,
    clubId: string
  ): Promise<unknown> {
    return this.$queryRaw(
      Prisma.sql`CALL CreatePlayerV2(${email}, ${phoneNumber}, ${firstName}, ${lastName}, ${dateOfBirth}, ${clubId})`
    );
  }

  /**
   * Function to call the Database procedure
   * `Get_Club_Memberships`.
   *
   * @TODO Remove use of database procedure and use
   * Prisma-only code.
   *
   * @param ownerEmail
   * @returns Promise<unknown>
   */
  public async GetClubMemberships(ownerEmail: string): Promise<unknown> {
    return this.$queryRaw(Prisma.sql`CALL Get_Club_Memberships(${ownerEmail}`);
  }

  /**
   * Function to call the Database procedure
   * `Get_Clubs`.
   *
   * @TODO Remove use of database procedure and use
   * Prisma-only code.
   *
   * @param ownerEmail
   * @returns Promise<unknown>
   */
  public async GetClubs(ownerEmail: string): Promise<unknown> {
    return this.$queryRaw(Prisma.sql`CALL Get_Clubs(${ownerEmail})`);
  }

  /**
   * Function to call the Database procedure
   * `GetGroupMemberships`.
   *
   * @TODO Remove use of database procedure and use
   * Prisma-only code.
   *
   * @param ownerEmail
   * @returns
   */
  public async GetGroupMemberships(ownerEmail: string): Promise<unknown> {
    return this.$queryRaw(Prisma.sql`CALL GetGroupMemberships(${ownerEmail})`);
  }

  /**
   * Function to call the Database procedure
   * `Get_Groups`.
   *
   * @TODO Remove use of database procedure and use
   * Prisma-only code.
   *
   * @param ownerEmail
   * @returns
   */
  public async GetGroups(ownerEmail: string): Promise<unknown> {
    return this.$queryRaw(Prisma.sql`CALL Get_Groups(${ownerEmail})`);
  }

  /**
   * Function to call the Database procedure
   * `GetPlayers`.
   *
   * @TODO Remove use of database procedure and use
   * Prisma-only code.
   *
   * @param ownerEmail
   * @returns
   */
  public async GetPlayers(ownerEmail: string): Promise<unknown> {
    return this.$queryRaw(Prisma.sql`CALL GetPlayers(${ownerEmail})`);
  }

  /**
   * Function to call the Database procedure
   * `GetPlayersWithoutGroup`.
   *
   * @TODO Remove use of database procedure and use
   * Prisma-only code.
   *
   * @param clubId
   * @param groupId
   * @returns Promise<unknown>
   */
  public async GetPlayersWithGroup(
    clubId: string,
    groupId: string
  ): Promise<unknown> {
    return this.$queryRaw(
      Prisma.sql`CALL GetPlayersWithGroup(${clubId}, ${groupId})`
    );
  }

  public async GetPlayersWithoutGroup(clubId: string): Promise<unknown> {
    return this.$queryRaw(Prisma.sql`CALL GetPlayersWithoutGroup(${clubId})`);
  }
}
