import {
    Args,
    Context,
    Mutation,
    Parent,
    Query,
    ResolveField,
    Resolver,
    Subscription,
} from '@nestjs/graphql';
import { Group } from './models/group.model';
import { PrismaService } from '../prisma/prisma.service';
import { GraphQLString } from 'graphql';
import * as seedWords from 'mnemonic-words';
import {
    NotFoundException,
    UnauthorizedException,
    UseGuards,
} from '@nestjs/common';
import { GroupAuthGuard } from '../auth/group-auth.guard';
import { Request } from 'express';
import { pubSub } from '../pubSub.instance';
import { AgentAuthGuard } from '../auth/agent-auth.guard';

@Resolver(() => Group)
export class GroupResolver {
    constructor(private prismaService: PrismaService) {}

    @Subscription(() => Group, {
        filter: (payload, variables) =>
            payload.updateToken.slug === variables.groupSlug,
    })
    async updateToken(
        @Args({ name: 'groupSlug', type: () => GraphQLString })
        groupSlug: string,
    ) {
        if (!groupSlug) throw new UnauthorizedException();

        const group = await this.prismaService.group.findFirst({
            where: {
                slug: groupSlug,
            },
        });

        if (!group) throw new NotFoundException('That Group does not exist');

        return pubSub.asyncIterator('updateToken');
    }

    @Mutation(() => Group)
    @UseGuards(AgentAuthGuard)
    async addToken(
        @Args({ name: 'groupSlug', type: () => GraphQLString })
        groupSlug: string,
    ) {
        let group = await this.prismaService.group.findFirst({
            where: { slug: groupSlug },
        });

        if (!group) throw new NotFoundException('That Group does not exist');

        group = await this.prismaService.group.update({
            where: {
                slug: groupSlug,
            },
            data: {
                tokens: { increment: 1 },
            },
        });

        await pubSub.publish('updateToken', { updateToken: group });

        return group;
    }

    @Mutation(() => Group)
    async createGroup(
        @Args({ name: 'name', type: () => GraphQLString }) name: string,
        @Args({ name: 'code', type: () => GraphQLString }) agentCode: string,
    ) {
        if (!agentCode) throw new UnauthorizedException();

        // TODO: refactor agent auth, modify auth guard

        const agent = await this.prismaService.agent.findFirst({
            where: {
                slug: agentCode,
            },
        });

        if (!agent) throw new NotFoundException('That Agent does not exist');

        function generateWord() {
            return seedWords[Math.round(Math.random() * seedWords.length)];
        }

        const slug = [
            generateWord(),
            generateWord(),
            generateWord(),
            generateWord(),
        ].join('-');

        return this.prismaService.group.create({
            data: {
                name,
                slug,
            },
        });
    }

    @Query(() => Group)
    @UseGuards(GroupAuthGuard)
    async getGroup(@Context() { req }: { req: Request }) {
        return req.group;
    }

    @ResolveField('unlocks')
    async unlocks(@Parent() group: Group) {
        const result = await this.prismaService.group.findFirst({
            where: { id: group.id },
            select: { unlocks: true },
        });

        return result.unlocks.map((unlock) => ({
            ...unlock,
            locked: false,
        }));
    }
}