import { Injectable } from '@nestjs/common';
import {
  db,
  Prisma,
  UserProfile,
  VisitType,
  Post,
  PostType,
  RolePerms,
  ResPerm,
  ObjectType,
  LectureType,
  CourseMethodSchema,
} from '@nice/common';
import { MessageService } from '../message/message.service';
import { BaseService } from '../base/base.service';
import { DepartmentService } from '../department/department.service';
import { setPostInfo, setPostRelation } from './utils';
import EventBus, { CrudOperation } from '@server/utils/event-bus';
import { BaseTreeService } from '../base/base.tree.service';
import { z } from 'zod';
import dayjs from 'dayjs';
import { OrderByArgs } from '../base/base.type';

@Injectable()
export class PostService extends BaseTreeService<Prisma.PostDelegate> {
  constructor(
    private readonly messageService: MessageService,
    private readonly departmentService: DepartmentService,
  ) {
    super(db, ObjectType.POST, 'postAncestry', true);
  }
  async createLecture(
    lecture: z.infer<typeof CourseMethodSchema.createLecture>,
    params: { staff?: UserProfile; tx?: Prisma.TransactionClient },
  ) {
    const { sectionId, type, title, content, resourceIds = [] } = lecture;
    const { staff, tx } = params;
    return await this.create(
      {
        data: {
          type: PostType.LECTURE,
          parentId: sectionId,
          content: content,
          title: title,
          authorId: params?.staff?.id,
          updatedAt: dayjs().toDate(),
          resources: {
            connect: resourceIds.map((fileId) => ({ fileId })),
          },
          meta: {
            type: type,
          },
        } as any,
      },
      { tx },
    );
  }
  async createSection(
    section: z.infer<typeof CourseMethodSchema.createSection>,
    params: {
      staff?: UserProfile;
      tx?: Prisma.TransactionClient;
    },
  ) {
    const { title, courseId, lectures } = section;
    const { staff, tx } = params;
    // Create section first
    const createdSection = await this.create(
      {
        data: {
          type: PostType.SECTION,
          parentId: courseId,
          title: title,
          authorId: staff?.id,
          updatedAt: dayjs().toDate(),
        } as any,
      },
      { tx },
    );
    // If lectures are provided, create them
    if (lectures && lectures.length > 0) {
      const lecturePromises = lectures.map((lecture) =>
        this.createLecture(
          {
            sectionId: createdSection.id,
            ...lecture,
          },
          params,
        ),
      );

      // Create all lectures in parallel
      await Promise.all(lecturePromises);
    }
    return createdSection;
  }
  async createCourse(
    args: {
      courseDetail: Prisma.PostCreateArgs;
    },
    params: { staff?: UserProfile; tx?: Prisma.TransactionClient },
  ) {
    
    const { courseDetail } = args;
    // If no transaction is provided, create a new one
    if (!params.tx) {
      return await db.$transaction(async (tx) => {
        const courseParams = { ...params, tx };
        // Create the course first
        const createdCourse = await this.create(courseDetail, courseParams);
        // If sections are provided, create them
        return createdCourse;
      });
    }
    // If transaction is provided, use it directly
    const createdCourse = await this.create(courseDetail, params);
    // If sections are provided, create them
    return createdCourse;
  }
  async create(
    args: Prisma.PostCreateArgs,
    params?: { staff?: UserProfile; tx?: Prisma.TransactionClient },
  ) {
    args.data.authorId = params?.staff?.id;
    args.data.updatedAt = dayjs().toDate();

    const result = await super.create(args);
    EventBus.emit('dataChanged', {
      type: ObjectType.POST,
      operation: CrudOperation.CREATED,
      data: result,
    });
    return result;
  }
  async update(args: Prisma.PostUpdateArgs, staff?: UserProfile) {
    //args.data.authorId = staff?.id;
    args.data.updatedAt = dayjs().toDate();
    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 });
          await this.setPerms(result, staff);
          await setPostInfo({ data: result });
        }
        // console.log(result);
        return result;
      },
    );
    return transDto;
  }

  async findManyWithCursor(args: Prisma.PostFindManyArgs, staff?: UserProfile) {
    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 });
          await this.setPerms(item, staff);
        }),
      );
      return { ...result, items };
    });
  }
  async findManyWithPagination(args: {
    page?: number;
    pageSize?: number;
    where?: Prisma.PostWhereInput;
    orderBy?: OrderByArgs<(typeof db.post)['findMany']>;
    select?: Prisma.PostSelect;
  }): Promise<{
    items: {
      id: string;
      type: string | null;
      level: string | null;
      state: string | null;
      title: string | null;
      subTitle: string | null;
      content: string | null;
      important: boolean | null;
      domainId: string | null;
      order: number | null;
      duration: number | null;
      rating: number | null;
      createdAt: Date;
      views: number;
      hates: number;
      likes: number;
      publishedAt: Date | null;
      updatedAt: Date;
      deletedAt: Date | null;
      authorId: string | null;
      parentId: string | null;
      hasChildren: boolean | null;
      meta: Prisma.JsonValue | null;
    }[];
    totalPages: number;
  }> {
    // super.updateOrder;
    return super.findManyWithPagination(args);
  }

  async updateOrderByIds(ids: string[]) {
    const posts = await db.post.findMany({
      where: { id: { in: ids } },
      select: { id: true, order: true },
    });
    const postMap = new Map(posts.map((post) => [post.id, post]));
    const orderedPosts = ids
      .map((id) => postMap.get(id))
      .filter((post): post is { id: string; order: number } => !!post);

    // 生成仅需更新的操作
    const updates = orderedPosts
      .map((post, index) => ({
        id: post.id,
        newOrder: index, // 按数组索引设置新顺序
        currentOrder: post.order,
      }))
      .filter(({ newOrder, currentOrder }) => newOrder !== currentOrder)
      .map(({ id, newOrder }) =>
        db.post.update({
          where: { id },
          data: { order: newOrder },
        }),
      );

    // 批量执行更新
    return updates.length > 0 ? await db.$transaction(updates) : [];
  }

  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 parentDeptIds =
      (await this.departmentService.getAncestorIds(staff.deptId)) || [];
    const orCondition: Prisma.PostWhereInput[] = [
      staff?.id && {
        authorId: staff.id,
      },
    ].filter(Boolean);

    if (orCondition?.length > 0) return orCondition;
    return undefined;
  }
  async softDeletePostDescendant(args:{ancestorId?:string}){
    const { ancestorId } = args
    const descendantIds = []
    await db.postAncestry.findMany({
      where:{
        ancestorId,
      },
      select:{
        descendantId:true
      }
    }).then(res=>{
      res.forEach(item=>{
        descendantIds.push(item.descendantId)
      })
    })
    console.log(descendantIds)
    const result = super.softDeleteByIds([...descendantIds,ancestorId])
    EventBus.emit('dataChanged', {
      type: ObjectType.POST,
      operation: CrudOperation.DELETED,
      data: result,
    });
    return result
  }
}
