import {
  Resolver,
  Query,
  Args,
  ResolveField,
  Parent,
  Mutation
} from '@nestjs/graphql';
import { UseGuards } from '@nestjs/common';
import { GraphQLError } from 'graphql';
import { Role } from '@prisma/client';

import { DatabaseService } from '../database/database.service';
import {
  Club,
  ClubCreateInput,
  ClubUpdateInput,
  ClubWhereInput,
  Game,
  Group,
  Player,
  User
} from '../@generated/prisma-nestjs-graphql';
import { CurrentUser } from '../current-user.decorator';
import { JwtAuthGuard } from '../authentication/jwt-auth.guard';
import { Roles } from '../authorization/roles.decorator';

@Resolver((of: any) => Club)
export class ClubResolver {
  constructor(private readonly $database: DatabaseService) {}

  @Query((returns) => Club)
  @UseGuards(JwtAuthGuard)
  public async clubById(
    @Args('id', { type: () => String }) id: string
  ): Promise<Club | null> {
    return this.$database.club.findUnique({
      where: {
        id
      }
    });
  }

  @Query((returns) => [Club])
  @UseGuards(JwtAuthGuard)
  public async clubsByOwner(
    @CurrentUser() user: User,
    @Args('filter', { type: () => ClubWhereInput, nullable: true })
    filter: ClubWhereInput
  ): Promise<Club[]> {
    return this.$database.club.findMany({
      where: {
        owner: {
          id: user.id
        },
        ...filter
      }
    });
  }

  @Query((returns) => [Club])
  @UseGuards(JwtAuthGuard)
  @Roles(Role.Admin)
  public async clubs(
    @Args('filter', { type: () => ClubWhereInput, nullable: true })
    filter: ClubWhereInput
  ): Promise<Club[]> {
    return this.$database.club.findMany({
      where: filter
    });
  }

  @Mutation((returns) => Club)
  @UseGuards(JwtAuthGuard)
  public async createClub(
    @CurrentUser() user: User,
    @Args('club', { type: () => ClubCreateInput })
    club: ClubCreateInput
  ): Promise<Club> {
    const { id } = user;

    return this.$database.club.create({
      data: Object.assign(club, {
        owner: {
          connect: {
            where: {
              id
            }
          }
        }
      })
    });
  }

  @Mutation((returns) => Club)
  @UseGuards(JwtAuthGuard)
  public async updateClub(
    @CurrentUser() user: User,
    @Args('data', { type: () => ClubUpdateInput, nullable: false })
    data: ClubUpdateInput
  ) {
    const { id } = user;

    const dbClub = await this.$database.club.findFirst({
      where: {
        ownerId: id,
        clubName: data.clubName as string
      }
    });

    if (dbClub) {
      return this.$database.club.update({
        where: {
          id: dbClub.id
        },
        data
      });
    } else {
      throw new GraphQLError('Club was not found in the database.');
    }
  }

  @ResolveField()
  public async owner(@Parent() club: Club): Promise<User | null> {
    const { ownerId } = club;

    return this.$database.user.findFirst({
      where: {
        id: ownerId
      }
    });
  }

  @ResolveField()
  public async games(@Parent() club: Club): Promise<Game[]> {
    const { id } = club;

    return this.$database.game.findMany({
      where: {
        club: {
          id
        }
      }
    });
  }

  @ResolveField()
  public async groups(@Parent() club: Club): Promise<Group[]> {
    const { id } = club;

    return this.$database.group.findMany({
      where: {
        club: {
          id
        }
      }
    });
  }

  @ResolveField()
  public async players(@Parent() club: Club): Promise<Player[]> {
    const { id } = club;

    return this.$database.player.findMany({
      where: {
        club: {
          id
        }
      }
    });
  }
}
