import { Test, TestingModule } from '@nestjs/testing';
import { NotFoundException, ConflictException, BadRequestException } from '@nestjs/common';
import { CommunityService } from '../community.service';
import { PrismaService } from '../../../prisma/prisma.service';
import { NotificationService } from '../../notifications/notification.service';

const selectUser = { id: true, nickname: true, avatar: true };

describe('CommunityService', () => {
  let service: CommunityService;
  let prisma: PrismaService;

  const mockPrisma = {
    post: {
      findMany: jest.fn(),
      findUnique: jest.fn(),
      create: jest.fn(),
      update: jest.fn(),
      delete: jest.fn(),
      count: jest.fn(),
    },
    comment: {
      create: jest.fn(),
    },
    postLike: {
      findFirst: jest.fn(),
      create: jest.fn(),
      delete: jest.fn(),
    },
    user: {
      findUnique: jest.fn(),
      update: jest.fn(),
    },
    follow: {
      findUnique: jest.fn(),
      findMany: jest.fn(),
      create: jest.fn(),
      delete: jest.fn(),
      count: jest.fn(),
    },
    circle: {
      findMany: jest.fn(),
      findUnique: jest.fn(),
      findFirst: jest.fn(),
      create: jest.fn(),
    },
    circleMember: {
      findUnique: jest.fn(),
      create: jest.fn(),
      delete: jest.fn(),
    },
  };

  const mockNotification = { create: jest.fn() };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        CommunityService,
        { provide: PrismaService, useValue: mockPrisma },
        { provide: NotificationService, useValue: mockNotification },
      ],
    }).compile();

    service = module.get<CommunityService>(CommunityService);
    prisma = module.get<PrismaService>(PrismaService);
    jest.clearAllMocks();
  });

  describe('findPosts', () => {
    it('should return paginated posts without tag filter', async () => {
      const mockPosts = [
        { id: 1, title: '帖子1', status: 'PUBLISHED' },
      ];
      mockPrisma.post.findMany.mockResolvedValue(mockPosts);
      mockPrisma.post.count.mockResolvedValue(1);

      const result = await service.findPosts({});

      expect(result).toEqual({ items: mockPosts, total: 1, page: 1, pageSize: 20 });
      expect(mockPrisma.post.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: { status: 'PUBLISHED' },
          skip: 0,
          take: 20,
          include: { user: { select: selectUser } },
        })
      );
    });

    it('should filter by tag', async () => {
      await service.findPosts({ tag: 'AI' });

      expect(mockPrisma.post.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({ tags: { contains: 'AI' } }),
        })
      );
    });
  });

  describe('findPostById', () => {
    it('should increment viewCount and return post', async () => {
      const mockPost = {
        id: 1,
        title: '测试帖子',
        status: 'PUBLISHED',
        user: { id: 1, nickname: '用户1' },
        comments: [],
      };
      mockPrisma.post.update.mockResolvedValue(mockPost);
      mockPrisma.post.findUnique.mockResolvedValue(mockPost);

      const result = await service.findPostById(1);

      expect(result).toEqual(mockPost);
      expect(mockPrisma.post.update).toHaveBeenCalledWith({
        where: { id: 1 },
        data: { viewCount: { increment: 1 } },
      });
    });
  });

  describe('createPost', () => {
    it('should create a new post', async () => {
      const mockPost = {
        id: 1,
        title: '新帖子',
        content: '内容',
        userId: 1,
      };
      mockPrisma.post.create.mockResolvedValue(mockPost);

      const result = await service.createPost(1, {
        title: '新帖子',
        content: '内容',
      });

      expect(result).toEqual(mockPost);
      expect(mockPrisma.post.create).toHaveBeenCalledWith({
        data: {
          userId: 1,
          title: '新帖子',
          content: '内容',
        },
        include: { user: { select: selectUser } },
      });
      expect(mockPrisma.user.update).toHaveBeenCalledWith({
        where: { id: 1 },
        data: { postCount: { increment: 1 } },
      });
    });
  });

  describe('addComment', () => {
    it('should add comment and increment commentCount', async () => {
      const mockPost = { id: 1, userId: 2 };
      mockPrisma.post.findUnique.mockResolvedValue(mockPost);

      const mockComment = { id: 1, content: '评论', userId: 1, postId: 1, user: { id: 1, nickname: '用户1', avatar: null } };
      mockPrisma.comment.create.mockResolvedValue(mockComment);

      const result = await service.addComment(1, 1, '评论');

      expect(result).toEqual(mockComment);
      expect(mockPrisma.comment.create).toHaveBeenCalledWith({
        data: { userId: 1, postId: 1, content: '评论', status: 'PENDING_REVIEW' },
        include: { user: { select: selectUser } },
      });
    });

    it('should throw NotFoundException if post not found', async () => {
      mockPrisma.post.findUnique.mockResolvedValue(null);

      await expect(service.addComment(1, 999, '评论')).rejects.toThrow(NotFoundException);
    });
  });

  describe('toggleLike', () => {
    it('should add like if not exists', async () => {
      mockPrisma.postLike.findFirst.mockResolvedValue(null);
      mockPrisma.postLike.create.mockResolvedValue({});
      mockPrisma.post.findUnique.mockResolvedValue({ id: 1, userId: 2, title: '帖子' });

      const result = await service.toggleLike(1, 1);

      expect(result).toEqual({ liked: true });
      expect(mockPrisma.postLike.create).toHaveBeenCalledWith({
        data: { userId: 1, postId: 1 },
      });
    });

    it('should remove like if exists', async () => {
      mockPrisma.postLike.findFirst.mockResolvedValue({ id: 1 });
      mockPrisma.postLike.delete.mockResolvedValue({});

      const result = await service.toggleLike(1, 1);

      expect(result).toEqual({ liked: false });
      expect(mockPrisma.postLike.delete).toHaveBeenCalledWith({
        where: { id: 1 },
      });
    });
  });

  describe('checkLike', () => {
    it('should return liked status', async () => {
      mockPrisma.postLike.findFirst.mockResolvedValue({ id: 1 });

      const result = await service.checkLike(1, 1);

      expect(result).toEqual({ liked: true });
    });

    it('should return not liked if no record', async () => {
      mockPrisma.postLike.findFirst.mockResolvedValue(null);

      const result = await service.checkLike(1, 1);

      expect(result).toEqual({ liked: false });
    });
  });
});
