import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'

import { getRedirectSubdomainPath } from '@things-factory/shell'
import { ProcessTemplate } from './process-template'
import { Process } from '../process/process'
import { NewProcessTemplate, ProcessTemplatePatch } from './process-template-type'
import { thumbnail } from '../../controllers/thumbnail'

@Resolver(ProcessTemplate)
export class ProcessTemplateMutation {
  @Directive('@transaction')
  @Directive('@privilege(category: "process-template", privilege: "mutation", domainOwnerGranted: true)')
  @Mutation(returns => ProcessTemplate, { description: 'To create new ProcessTemplate' })
  async createProcessTemplate(
    @Arg('processTemplate') processTemplate: NewProcessTemplate,
    @Ctx() context: ResolverContext
  ): Promise<ProcessTemplate> {
    const { domain, user, tx } = context.state
    const repository = tx.getRepository(ProcessTemplate)

    const oldProcessTemplate: ProcessTemplate = await repository.findOneBy({
      name: processTemplate.name,
      domain: { id: domain.id }
    })

    if (oldProcessTemplate) {
      throw new Error(context.t('error.process-template name is already taken', { name: processTemplate.name }))
    }

    const newProcessTemplate: Partial<ProcessTemplate> = {
      ...processTemplate
    }

    const base64 = await thumbnail({
      model: processTemplate.model,
      context
    })

    if (base64) {
      newProcessTemplate.thumbnail = 'data:image/png;base64,' + base64.toString('base64')
    } else {
      newProcessTemplate.thumbnail = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
    }

    return await repository.save({
      domain,
      ...newProcessTemplate,
      creator: user,
      updater: user
    })
  }

  @Directive('@transaction')
  @Directive('@privilege(category: "process-template", privilege: "mutation", domainOwnerGranted: true)')
  @Mutation(returns => ProcessTemplate, { description: 'To modify ProcessTemplate information' })
  async updateProcessTemplate(
    @Arg('id') id: string,
    @Arg('patch') patch: ProcessTemplatePatch,
    @Ctx() context: ResolverContext
  ): Promise<ProcessTemplate> {
    const { domain, user, tx } = context.state
    const repository = tx.getRepository(ProcessTemplate)

    const processTemplate = await repository.findOne({
      where: { domain: { id: domain.id }, id }
    })

    if (patch.model) {
      const base64 = await thumbnail({
        model: patch.model,
        context
      })

      if (base64) {
        patch.thumbnail = 'data:image/png;base64,' + base64.toString('base64')
      } else {
        patch.thumbnail = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
      }
    }

    return await repository.save({
      ...processTemplate,
      ...patch,
      updater: user
    })
  }

  @Directive('@transaction')
  @Directive('@privilege(category: "process-template", privilege: "mutation", domainOwnerGranted: true)')
  @Mutation(returns => Boolean, { description: 'To delete ProcessTemplate' })
  async deleteProcessTemplate(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
    const { domain, tx } = context.state

    await tx.getRepository(ProcessTemplate).delete({ domain: { id: domain.id }, id })

    return true
  }

  @Directive('@transaction')
  @Directive('@privilege(category: "process-template", privilege: "mutation", domainOwnerGranted: true)')
  @Mutation(returns => ProcessTemplate, {
    description: 'To register a process as a process template with the given ID'
  })
  async registerProcessAsTemplate(
    @Arg('id', { description: 'process Id to be regiestered' }) id: string,
    @Arg('name', { description: 'name of process template to be regiestered' }) name: string,
    @Arg('description', { description: 'description of process template to be regiestered' }) description: string,
    @Arg('visibility', { description: 'visibility of process template to be regiestered' }) visibility: string,
    @Ctx() context: ResolverContext
  ): Promise<ProcessTemplate> {
    const { domain, user, notify, tx } = context.state

    const boardTemplateRepository = tx.getRepository(ProcessTemplate)

    const process = await tx.getRepository(Process).findOne({
      where: { domain: { id: domain.id }, id }
    })

    if (!process) {
      throw `Process given id(${id}) is not found`
    }

    name ||= process.name

    const { model, thumbnail } = process

    const processTemplate = await boardTemplateRepository.findOne({
      where: { domain: { id: domain.id }, name }
    })

    const registered = processTemplate
      ? await boardTemplateRepository.save({
          ...processTemplate,
          name,
          description: description || processTemplate.description,
          visibility: visibility || processTemplate.visibility,
          model,
          thumbnail,
          updater: user
        })
      : await boardTemplateRepository.save({
          domain: { id: domain.id },
          name,
          description: description || process.description,
          model,
          thumbnail,
          visibility: visibility || 'private',
          updater: user,
          creator: user
        })

    notify &&
      notify({
        mode: 'in-app',
        title: `ProcessTemplate '${registered.name}' registered`,
        body: `ProcessTemplate '${registered.name}' registered by ${user.name}\n${registered.description}`,
        image: getRedirectSubdomainPath(context, domain.subdomain, `/process-template-thumbnail/${registered.id}`),
        url: getRedirectSubdomainPath(context, domain.subdomain, `/process-template-viewer/${registered.id}`)
      })

    return registered
  }
}
