import { Brackets, In } from 'typeorm'
import { Resolver, Query, FieldResolver, Root, Args, Arg, Ctx, Directive } from 'type-graphql'
import { Attachment } from '@things-factory/attachment-base'
import { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'
import { User } from '@things-factory/auth-base'
import { DesignPackage } from './design-package'
import { DesignPackageList } from './design-package-type'

@Resolver(DesignPackage)
export class DesignPackageQuery {
  @Query(returns => DesignPackage!, { nullable: true, description: 'To fetch a DesignPackage' })
  @Directive('@privilege(category: "design-package", privilege: "query", domainOwnerGranted: true)')
  async designPackage(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<DesignPackage> {
    const { domain, user } = context.state

    const qb = await getRepository(DesignPackage)
      .createQueryBuilder('DesignPackage')
      .where('id=:id', { id })
      .andWhere(
        new Brackets(qb => {
          qb.where({ visibility: 'public' })
            .orWhere({
              visibility: 'domain',
              domain: { id: In([domain.id, domain.parentId]) }
            })
            .orWhere({
              visibility: 'private',
              creator: { id: user.id }
            })
        })
      )

    return qb.getOne()

    // return await getRepository(DesignPackage).findOne({
    //   where: {
    //     domain: { id: In([domain.id, domain.parentId].filter(Boolean)) },
    //     id
    //   }
    // })
  }

  @Query(returns => DesignPackageList, { description: 'To fetch DesignPackages created by me' })
  async designPackagesCreatedByMe(
    @Args() params: ListParam,
    @Ctx() context: ResolverContext
  ): Promise<DesignPackageList> {
    const { user } = context.state

    const queryBuilder = getQueryBuilderFromListParams({
      repository: getRepository(DesignPackage),
      params,
      alias: 'template',
      searchables: ['name', 'description']
    }).andWhere('template.creator = :user', { user: user.id })

    const [items, total] = await queryBuilder.getManyAndCount()

    return { items, total }
  }

  @Query(returns => DesignPackageList, { description: 'To fetch multiple DesignPackages' })
  @Directive('@privilege(category: "design-package", privilege: "query", domainOwnerGranted: true)')
  async designPackages(@Args() params: ListParam, @Ctx() context: ResolverContext): Promise<DesignPackageList> {
    const { domain, user } = context.state
    /* 
      리스트에 포함되는 보드템플릿들 
      - visibility가 public 인 경우
      - visibility가 domain이며 컨텍스트의 도메인과 같은 경우
      - visibility가 private 이며, creator가 나인 경우
    */

    const queryBuilder = getQueryBuilderFromListParams({
      params,
      repository: await getRepository(DesignPackage),
      searchables: ['name', 'description']
    }).andWhere(
      new Brackets(qb => {
        qb.where({ visibility: 'public' })
          .orWhere({
            visibility: 'domain',
            domain: { id: In([domain.id, domain.parentId]) }
          })
          .orWhere({
            visibility: 'private',
            creator: { id: user.id }
          })
      })
    )

    const [items, total] = await queryBuilder.getManyAndCount()

    return { items, total }
  }

  @FieldResolver(type => Boolean)
  async mine(@Root() designPackage: DesignPackage, @Ctx() context: ResolverContext): Promise<boolean> {
    const { creatorId } = designPackage
    const { user } = context.state

    return creatorId == user.id
  }

  @FieldResolver(type => Domain)
  async domain(@Root() designPackage: DesignPackage): Promise<Domain> {
    return designPackage.domainId && (await getRepository(Domain).findOneBy({ id: designPackage.domainId }))
  }

  @FieldResolver(type => User)
  async updater(@Root() designPackage: DesignPackage): Promise<User> {
    return designPackage.updaterId && (await getRepository(User).findOneBy({ id: designPackage.updaterId }))
  }

  @FieldResolver(type => User)
  async creator(@Root() designPackage: DesignPackage): Promise<User> {
    return designPackage.creatorId && (await getRepository(User).findOneBy({ id: designPackage.creatorId }))
  }
}
