import {Args, Context, Int, Mutation, Resolver} from '@nestjs/graphql';
import { Organizer } from '../models/Organizer';
import { Client } from '../../client';
import { OrganizerService } from '../organizer.service';
import { HttpException } from '@nestjs/common';
import {UUID} from '../../global/scalars/UUID'
import {pubsub} from '../../main'

@Resolver(() => Organizer)
export class OrganizerResolverM {
  constructor(
    private readonly service: OrganizerService,
  ) {}

  @Mutation(() => Organizer, { nullable: false })
  async OrganizerRegister(
    @Context('client') client: Client,
    @Args('name') name: string,
    @Args('plz', { type: () => Int, nullable: true }) plz?: number,
    @Args('ort', { nullable: true }) ort?: string,
  ): Promise<Organizer> {
    const filter: any = {}

    if (name) filter.name = name;
    if (plz) filter.plz = plz;
    if (ort) filter.ort = ort;

    let tmp = await this.service.find(filter);
    if (tmp.length === 0)
      return this.service.create(client, name, plz, ort);
    return tmp[0];
  }

  @Mutation(() => Organizer, { nullable: false })
  async OrganizerUpdate(
    @Context('client') client: Client,
    @Args('id', { type: () => UUID, nullable: false }) id: UUID,
    @Args('name', { nullable: true }) name?: string,
    @Args('plz', { type: () => Int, nullable: true }) plz?: number,
    @Args('ort', { nullable: true }) ort?: string
  ): Promise<Organizer> {
    if (!client.isMaster() && !client.isAdmin(id)) throw new HttpException('Access denied', 403)

    const tmp = await this.service.findOneById(id);

    const set: {
      name?: string,
      plz?: number,
      ort?: string,
    } = {};

    if (name !== undefined && name !== tmp.name) set.name = name
    if (plz !== undefined && plz !== tmp.plz) set.plz = plz
    if (ort !== undefined && ort !== tmp.ort) set.ort = ort

    if (Object.values(set).length === 0) return tmp;

    const neu = await this.service.update(client, tmp._id, {$set: set});
    pubsub.publish('OrganizerUpdated', { OrganizerUpdated: neu });
    return neu;
  }

  @Mutation(() => Organizer, { nullable: false })
  async OrganizerCreate(
    @Context('client') client: Client,
    @Args('name') name: string,
    @Args('plz', { type: () => Int, nullable: true }) plz?: number,
    @Args('ort', { nullable: true }) ort?: string,
  ): Promise<Organizer> {
    if (!client.isMaster()) throw new HttpException('Access denied', 403);

    const neu = await this.service.insert(client, { name, plz, ort });

    pubsub.publish('OrganizerUpdated', { OrganizerUpdated: neu });

    return neu;
  }

  @Mutation(() => UUID, { nullable: false })
  async OrganizerDelete(
    @Context('client') client: Client,
    @Args('id', { type: () => UUID, nullable: false }) id: UUID
  ): Promise<UUID> {
    if (!client.isMaster() && !client.isAdmin(id)) throw new HttpException('Access denied', 403);

    this.service.delete(client, id);

    pubsub.publish('OrganizerDeleted', { OrganizerDeleted: id });

    return id;
  }

  @Mutation(() => Organizer, { nullable: false })
  async OrganizerUpdateMakeOrganizer(
    @Context('client') client: Client,
    @Args('id', { type: () => UUID, nullable: false }) id: UUID,
    @Args('person', { type: () => UUID, nullable: false }) person: UUID
  ): Promise<Organizer> {
    if (!client.isMaster() && !client.isAdmin(id)) throw new HttpException('Access denied', 403);

    let tmp = await this.service.findOneById(id);

    if (tmp._admins.find(a => a === person)) {
      tmp = await this.service.update(client, id,
        {
          $pull: { '_admins': person },
          $push: { '_organizers': person }
        }
      )
    } else if (tmp._pending.find(p => p === person)) {
      tmp = await this.service.update(client, id,
        {
          $pull: { '_pending': person },
          $push: { '_organizers': person }
        }
      )
    } else {
      throw new HttpException('Person not found', 404)
    }

    pubsub.publish('OrganizerUpdated', { OrganizerUpdated: tmp });

    return tmp
  }

  @Mutation(() => Organizer, { nullable: false })
  async OrganizerUpdateMakeAdmin(
    @Context('client') client: Client,
    @Args('id', { type: () => UUID, nullable: false }) id: UUID,
    @Args('person', { type: () => UUID, nullable: false }) person: UUID
  ): Promise<Organizer> {
    if (!client.isMaster() && !client.isAdmin(id)) throw new HttpException('Access denied', 403);

    let tmp = await this.service.findOneById(id);

    if (tmp._organizers.find(o => o === person)) {
      tmp = await this.service.update(client, id,
        {
          $pull: { '_organizers': person },
          $push: { '_admins': person }
        }
      )
    } else if (tmp._pending.find(p => p === person)) {
      tmp = await this.service.update(client, id,
        {
          $pull: { '_pending': person },
          $push: { '_admins': person }
        }
      )
    } else {
      throw new HttpException('Person not found', 404)
    }

    pubsub.publish('OrganizerUpdated', { OrganizerUpdated: tmp });

    return tmp
  }

  @Mutation(() => Organizer, { nullable: false })
  async OrganizerUpdateDeletePerson(
    @Context('client') client: Client,
    @Args('id', { type: () => UUID, nullable: false }) id: UUID,
    @Args('person', { type: () => UUID, nullable: false }) person: UUID
  ): Promise<Organizer> {
    if (!client.isMaster() && !client.isAdmin(id)) throw new HttpException('Access denied', 403);

    let tmp = await this.service.findOneById(id);

    if (tmp._admins.find(a => a === person)) {
      tmp = await this.service.update(client, id,
        {
          $pull: { '_admins': person },
        }
      )
    } else if (tmp._organizers.find(o => o === person)) {
      tmp = await this.service.update(client, id,
        {
          $pull: { '_organizers': person },
        }
      )
    } else if (tmp._pending.find(p => p === person)) {
      tmp = await this.service.update(client, id,
        {
          $pull: { '_pending': person },
        }
      )
    } else {
      throw new HttpException('Person not found', 404)
    }

    pubsub.publish('OrganizerUpdated', { OrganizerUpdated: tmp });

    return tmp
  }
}
