import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'
import { In } from 'typeorm'

import { User } from '@things-factory/auth-base'
import { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'

import { ProcessGroup } from '../process-group/process-group'
import { Process } from './process'
import { ProcessHistory } from './process-history'
import { ProcessList } from './process-type'

@Resolver(Process)
export class ProcessQuery {
  @Query(returns => Process, { description: 'To fetch a Process' })
  @Directive('@privilege(category: "process", privilege: "query", domainOwnerGranted: true)')
  async process(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Process> {
    const { domain } = context.state

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

  @Query(returns => Process, { nullable: true, description: 'To fetch a Process Model by name' })
  @Directive('@privilege(category: "process", privilege: "query", domainOwnerGranted: true)')
  async processByName(@Arg('name') name: string, @Ctx() context: ResolverContext): Promise<Process> {
    const { domain } = context.state

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

  @Query(returns => ProcessList, { description: 'To fetch multiple Processes' })
  @Directive('@privilege(category: "process", privilege: "query", domainOwnerGranted: true)')
  async processes(@Args() params: ListParam, @Ctx() context: ResolverContext): Promise<ProcessList> {
    const { domain } = context.state

    const queryBuilder = getQueryBuilderFromListParams({
      repository: getRepository(Process),
      params,
      domain,
      searchables: ['name', 'description']
    })

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

    return { items, total }
  }

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

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

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

    return { items, total }
  }

  @Query(returns => [ProcessHistory], { description: 'To fetch a Process Versions' })
  @Directive('@privilege(category: "process", privilege: "query", domainOwnerGranted: true)')
  async processVersions(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<ProcessHistory[]> {
    const { domain } = context.state

    return await getRepository(ProcessHistory).find({
      where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, originalId: id },
      relations: ['updater'],
      order: { version: 'DESC' },
      take: 10
    })
  }

  @Query(returns => ProcessHistory, { description: 'To fetch the latest Process published' })
  @Directive('@privilege(category: "process", privilege: "query", domainOwnerGranted: true)')
  async processPublished(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<ProcessHistory> {
    const { domain } = context.state

    return await getRepository(ProcessHistory).findOne({
      where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, originalId: id },
      order: { version: 'DESC' }
    })
  }

  @FieldResolver(type => ProcessGroup)
  async group(@Root() process: Process) {
    return (
      process.groupId &&
      (await getRepository(ProcessGroup).findOneBy({
        id: process.groupId
      }))
    )
  }

  @FieldResolver(type => Domain)
  async domain(@Root() process: Process) {
    return (
      process.domainId &&
      (await getRepository(Domain).findOneBy({
        id: process.domainId
      }))
    )
  }

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

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