import { Injectable } from '@nestjs/common';
import {
  db,
  Prisma,
  UserProfile,
  Post,
  RolePerms,
  ResPerm,
  ObjectType,
  PostType,
} from '@nice/common';
import { MessageService } from '../message/message.service';
import { BaseService } from '../base/base.service';
import { DepartmentService } from '../department/department.service';
import { setPostRelation, updatePostState } from './utils';
import EventBus, { CrudOperation } from '@server/utils/event-bus';
import { DefaultArgs } from '@prisma/client/runtime/library';

@Injectable()
export class PostService extends BaseService<Prisma.PostDelegate> {
  constructor(
    private readonly messageService: MessageService,
    private readonly departmentService: DepartmentService,
  ) {
    super(db, ObjectType.POST);
  }
  onModuleInit() {
    EventBus.on('updatePostState', ({ id }) => {
      updatePostState(id);
    });
  }
  async create(
    args: Prisma.PostCreateArgs,
    params: { staff?: UserProfile; tx?: Prisma.PostDelegate },
  ) {
    args.data.authorId = params?.staff?.id;
    args.data.updatedAt = new Date();
    // args.data.resources
    const result = await super.create(args);
    await this.updateParentTimestamp(result?.parentId);
    EventBus.emit('dataChanged', {
      type: ObjectType.POST,
      operation: CrudOperation.CREATED,
      data: result,
    });
    if (args.data.authorId && args?.data?.type === PostType.POST_COMMENT) {
      EventBus.emit('updatePostState', {
        id: result?.id,
      });
    }
    return result;
  }
  async update(args: Prisma.PostUpdateArgs, staff?: UserProfile) {
    args.data.authorId = staff?.id;
    args.data.updatedAt = new Date();
    const result = await super.update(args);
    EventBus.emit('dataChanged', {
      type: ObjectType.POST,
      operation: CrudOperation.UPDATED,
      data: result,
    });
    return result;
  }
  async findFirst(
    args?: Prisma.PostFindFirstArgs,
    staff?: UserProfile,
    clientIp?: string,
  ) {
    const transDto = await this.wrapResult(
      super.findFirst(args),
      async (result) => {
        if (result) {
          await setPostRelation({ data: result, staff, clientIp });
          await this.setPerms(result, staff);
        }

        return result;
      },
    );
    return transDto;
  }
  async findManyWithCursor(
    args: Prisma.PostFindManyArgs,
    staff?: UserProfile,
    clientIp?: string,
  ) {
    if (!args.where) args.where = {};
    args.where.OR = await this.preFilter(args.where.OR, staff);
    return this.wrapResult(super.findManyWithCursor(args), async (result) => {
      const { items } = result;
      await Promise.all(
        items.map(async (item) => {
          await setPostRelation({ data: item, staff, clientIp });
          await this.setPerms(item, staff);
        }),
      );

      return { ...result, items };
    });
  }
  async findManyWithPagination(
    args: {
      page?: number;
      pageSize?: number;
      where?: Prisma.PostWhereInput;
      select?: Prisma.PostSelect<DefaultArgs>;
      orderBy?: Prisma.PostOrderByWithRelationInput;
    },
    staff?: UserProfile,
    clientIp?: string,
  ) {
    if (!args.where) args.where = {};
    args.where.OR = await this.preFilter(args.where.OR, staff);
    return this.wrapResult(
      super.findManyWithPagination(args as any),
      async (result) => {
        const { items } = result;
        await Promise.all(
          items.map(async (item) => {
            await setPostRelation({ data: item, staff, clientIp });
            await this.setPerms(item, staff);
          }),
        );
        return { ...result, items };
      },
    );
  }
  protected async setPerms(data: Post, staff?: UserProfile) {
    if (!staff) return;
    const perms: ResPerm = {
      delete: false,
    };
    const isMySelf = data?.authorId === staff?.id;
    // const isDomain = staff.domainId === data.domainId;
    const setManagePermissions = (perms: ResPerm) => {
      Object.assign(perms, {
        delete: true,
        // edit: true,
      });
    };
    if (isMySelf) {
      perms.delete = true;
      // perms.edit = true;
    }
    staff.permissions.forEach((permission) => {
      switch (permission) {
        case RolePerms.MANAGE_ANY_POST:
          setManagePermissions(perms);
          break;
        // case RolePerms.MANAGE_DOM_POST:
        //   if (isDomain) {
        //     setManagePermissions(perms);
        //   }
        //   break;
      }
    });
    Object.assign(data, { perms });
  }
  async preFilter(OR?: Prisma.PostWhereInput[], staff?: UserProfile) {
    const preFilter = (await this.getPostPreFilter(staff)) || [];
    const outOR = OR ? [...OR, ...preFilter].filter(Boolean) : preFilter;
    return outOR?.length > 0 ? outOR : undefined;
  }
  async getPostPreFilter(staff?: UserProfile) {
    if (!staff) return;
    // const { deptId, domainId } = staff;
    if (
      staff.permissions.includes(RolePerms.READ_ANY_POST) ||
      staff.permissions.includes(RolePerms.MANAGE_ANY_POST)
    ) {
      return undefined;
    }
    const orCondition: Prisma.PostWhereInput[] = [
      staff?.id && {
        authorId: staff.id,
      },
      {
        isPublic: true,
      },
      staff?.id && {
        receivers: {
          some: {
            id: staff.id,
          },
        },
      },
    ].filter(Boolean);

    if (orCondition?.length > 0) return orCondition;
    return undefined;
  }
  /**
   * 更新父帖子的时间戳
   * 当子帖子被创建时，自动更新父帖子的更新时间
   * @param parentId 父帖子的ID
   */
  async updateParentTimestamp(parentId: string | undefined) {
    if (!parentId) {
      return;
    }
    await this.update({
      where: {
        id: parentId,
      },
      data: {}, // 空对象会自动更新 updatedAt 时间戳
    });
  }
}
