import {
  ACAttrFilter,
  ACGuard,
  ApiPaginatedResponse,
  diskStorage,
  PageDto,
  PageMetaDto,
  PageOptionsDto,
  pick,
  UserType,
} from '@gadmin2n/nest-common';
import {
  Body,
  Controller,
  Delete,
  Inject,
  NotFoundException,
  Param,
  ParseIntPipe,
  Patch,
  Post,
  Req,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import {
  ApiCreatedResponse,
  ApiNotFoundResponse,
  ApiOkResponse,
  ApiTags,
} from '@nestjs/swagger';
import { Prisma } from '@prisma/client';
import { UseRoles } from 'nest-access-control';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
import { CreateWorkflowNodeInstanceDto } from '../../generated/workflowNodeInstance/dto/create-workflowNodeInstance.dto';
import { UpdateWorkflowNodeInstanceDto } from '../../generated/workflowNodeInstance/dto/update-workflowNodeInstance.dto';
import {
  EffectedCount,
  UploudResponse,
  WorkflowNodeInstanceFindManyArgs,
  WorkflowNodeInstanceWhereSelect,
} from '../../generated/workflowNodeInstance/dto/workflowNodeInstance-types.dto';
import { WorkflowNodeInstanceEntity } from '../../generated/workflowNodeInstance/entities/workflowNodeInstance.entity';
import { WorkflowNodeInstanceService } from './workflowNodeInstance.service';

import { ConfigService } from '@nestjs/config';
import { FileInterceptor } from '@nestjs/platform-express';

@UseGuards(ACGuard)
@Controller('workflowNodeInstance')
@ApiTags('workflowNodeInstance')
export class WorkflowNodeInstanceController {
  constructor(
    private readonly workflowNodeInstanceService: WorkflowNodeInstanceService,
    private readonly config: ConfigService,
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
  ) {}

  @Post('createOne')
  @UseRoles({
    resource: 'workflowNodeInstance',
    action: 'create',
    possession: 'any',
  })
  @ApiCreatedResponse({ type: WorkflowNodeInstanceEntity })
  async createOne(
    @Body() body: CreateWorkflowNodeInstanceDto,
    @ACAttrFilter() acAttrFilter,
    @Req() request: Request & { user: UserType },
  ) {
    body.creator = request.user.username;
    return new WorkflowNodeInstanceEntity(
      await this.workflowNodeInstanceService.createOne({
        data: acAttrFilter(body),
      }),
    );
  }

  @Post('findMany')
  @UseRoles({
    resource: 'workflowNodeInstance',
    action: 'read',
    possession: 'any',
  })
  @ApiPaginatedResponse(WorkflowNodeInstanceEntity)
  async findMany(
    @Body() body: WorkflowNodeInstanceFindManyArgs,
    @ACAttrFilter() acAttrFilter,
  ): Promise<PageDto<WorkflowNodeInstanceEntity>> {
    const { itemCount, entities } =
      await this.workflowNodeInstanceService.findMany(
        body as Prisma.WorkflowNodeInstanceFindManyArgs,
      );
    const { skip = 0, take = 10 } =
      body as Prisma.WorkflowNodeInstanceFindManyArgs;
    const pageOptionsDto: PageOptionsDto = { skip, take };
    const pageMetaDto = new PageMetaDto({ itemCount, pageOptionsDto });

    return new PageDto(
      entities.map(
        (item) => new WorkflowNodeInstanceEntity(acAttrFilter(item)),
      ),
      pageMetaDto,
    );
  }

  @Post('/findUnique/:id')
  @UseRoles({
    resource: 'workflowNodeInstance',
    action: 'read',
    possession: 'any',
  })
  @ApiOkResponse({ type: WorkflowNodeInstanceEntity })
  @ApiNotFoundResponse({ description: 'Cannot found this record.' })
  async findUnique(
    @Param('id', ParseIntPipe) id: number,
    @Body() body: WorkflowNodeInstanceWhereSelect,
    @ACAttrFilter() acAttrFilter,
  ) {
    const item = await this.workflowNodeInstanceService.findUnique(id, body);
    if (!item) {
      throw new NotFoundException();
    }
    return new WorkflowNodeInstanceEntity(acAttrFilter(item));
  }

  @Patch('updateUnique/:id')
  @UseRoles({
    resource: 'workflowNodeInstance',
    action: 'update',
    possession: 'any',
  })
  @ApiOkResponse({ type: WorkflowNodeInstanceEntity })
  @ApiNotFoundResponse({ description: 'Cannot found this record.' })
  async updateUnique(
    @Param('id', ParseIntPipe) id: number,
    @Body() body: UpdateWorkflowNodeInstanceDto,
    @ACAttrFilter() acAttrFilter,
  ) {
    /* 
    const origin = (await this.workflowNodeInstanceService.findUnique(id, {
      id: true,
      // 这里需要根据实际情况填写
    })) as WorkflowNodeInstanceEntity | null;
    if (!origin) {
      throw new NotFoundException();
    }
    // 需要兼容从列表页change_value入口进来的情况，这里进来的是只有少量字段的数据
    const newBody = {
      ...origin,
      ...body,
    };

    // 其他个性化逻辑
    */

    const item = await this.workflowNodeInstanceService.updateUnique(
      id,
      acAttrFilter(body),
    );
    return new WorkflowNodeInstanceEntity(acAttrFilter(item));
  }

  @Delete('deleteUnique/:id')
  @UseRoles({
    resource: 'workflowNodeInstance',
    action: 'delete',
    possession: 'any',
  })
  @ApiOkResponse({ type: EffectedCount })
  @ApiNotFoundResponse({ description: 'Cannot found this record.' })
  async deleteUnique(@Param('id', ParseIntPipe) id: number) {
    const res = await this.workflowNodeInstanceService.deleteUnique(id);
    if (!res.count) {
      throw new NotFoundException();
    }
    return res;
  }

  @Post(':id/references')
  @UseRoles({
    resource: 'workflowNodeInstance',
    action: 'read',
    possession: 'any',
  })
  @ApiOkResponse({ description: 'List of workflows referencing this instance' })
  async findReferences(@Param('id', ParseIntPipe) id: number) {
    return this.workflowNodeInstanceService.findReferences(id);
  }

  @Post('upload')
  @UseRoles({
    resource: 'workflowNodeInstance',
    action: 'create',
    possession: 'any',
  })
  @ApiCreatedResponse({ type: UploudResponse })
  @UseInterceptors(FileInterceptor('file'))
  async uploadFile(@UploadedFile() file: Express.Multer.File) {
    const fileName = await diskStorage(
      file,
      this.config.get('media')['rootDir'] + '/workflowNodeInstance/',
    );

    return {
      ...pick(file, ['mimetype', 'originalname', 'size']),
      url:
        this.config.get('media')['address'] +
        '/workflowNodeInstance/' +
        fileName,
      name: fileName,
    };
  }
}
