import { BadRequestException } from '@nestjs/common';
import { AuditService } from '../../common/audit/audit.service';
import { NotificationService } from '../notification/notification.service';
import { CommentService } from './comment.service';

describe('CommentService', () => {
  const makeService = () => {
    const prisma = {
      comment: {
        create: jest.fn(),
        findUnique: jest.fn(),
        update: jest.fn(),
      },
      projectMember: {
        findMany: jest.fn(),
      },
    };
    const notifications = {
      createMany: jest.fn(),
    } as unknown as NotificationService;
    const audit = {
      record: jest.fn(),
    } as unknown as AuditService;
    return {
      prisma,
      notifications,
      audit,
      service: new CommentService(prisma as any, notifications, audit),
    };
  };

  it('creates a polymorphic comment, extracts @mentions, notifies mentioned members, and writes audit', async () => {
    const { prisma, notifications, audit, service } = makeService();
    prisma.projectMember.findMany.mockResolvedValue([
      { userId: 'm-alice', user: { id: 'm-alice', name: 'Alice' } },
      { userId: 'm-bob', user: { id: 'm-bob', name: 'Bob' } },
    ]);
    prisma.comment.create.mockResolvedValue({
      id: 'comment-1',
      entityType: 'dev_task',
      entityId: 'task-1',
      mentionedMemberIds: ['m-alice', 'm-bob'],
    });

    const result = await service.create({
      actorId: 'm-author',
      entityType: 'dev_task',
      entityId: 'task-1',
      entityVersionId: 'ver-1',
      projectId: 'project-1',
      versionId: 'ver-1',
      content: '请 @Alice 看一下接口，Bob 也同步一下',
      mentionMemberIds: ['m-bob'],
    });

    expect(result.id).toBe('comment-1');
    expect(prisma.comment.create).toHaveBeenCalledWith({
      data: expect.objectContaining({
        entityType: 'dev_task',
        entityId: 'task-1',
        authorId: 'm-author',
        mentionedMemberIds: ['m-alice', 'm-bob'],
      }),
    });
    expect((notifications.createMany as jest.Mock)).toHaveBeenCalledWith([
      expect.objectContaining({ recipientId: 'm-alice', type: 'mention', resourceType: 'comment', resourceId: 'comment-1' }),
      expect.objectContaining({ recipientId: 'm-bob', type: 'mention', resourceType: 'comment', resourceId: 'comment-1' }),
    ]);
    expect((audit.record as jest.Mock)).toHaveBeenCalledWith(expect.objectContaining({
      actorId: 'm-author',
      action: 'comment.created',
      resourceType: 'comment',
      resourceId: 'comment-1',
    }));
  });

  it('soft deletes a comment and writes audit', async () => {
    const { prisma, audit, service } = makeService();
    prisma.comment.findUnique.mockResolvedValue({
      id: 'comment-1',
      authorId: 'm-author',
      entityType: 'bug',
      entityId: 'bug-1',
      deletedAt: null,
    });
    prisma.comment.update.mockResolvedValue({ id: 'comment-1', deletedAt: new Date('2026-07-08T08:00:00.000Z') });

    await service.remove('comment-1', 'm-author');

    expect(prisma.comment.update).toHaveBeenCalledWith({
      where: { id: 'comment-1' },
      data: { deletedAt: expect.any(Date) },
    });
    expect((audit.record as jest.Mock)).toHaveBeenCalledWith(expect.objectContaining({
      actorId: 'm-author',
      action: 'comment.deleted',
      resourceType: 'comment',
      resourceId: 'comment-1',
    }));
  });

  it('rejects unsupported comment entity types', async () => {
    const { service } = makeService();

    await expect(service.create({
      actorId: 'm-author',
      entityType: 'task',
      entityId: 'task-1',
      content: 'legacy task comment',
    } as any)).rejects.toBeInstanceOf(BadRequestException);
  });
});
