// Mock CouchDB service for testing
const mockCouchdbService = {
  findUserById: jest.fn(),
  getById: jest.fn(),
  createDocument: jest.fn(),
  updateDocument: jest.fn(),
  findByType: jest.fn(),
  find: jest.fn(),
  deleteDocument: jest.fn(),
  updatePost: jest.fn(),
  initialize: jest.fn(),
  isReady: jest.fn().mockReturnValue(true),
  shutdown: jest.fn()
};

// Mock the service module
jest.mock('../../services/couchdbService', () => mockCouchdbService);

const Comment = require('../../models/Comment');

describe('Comment Model', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    // Reset all mocks to ensure clean state
    mockCouchdbService.findUserById.mockReset();
    mockCouchdbService.getById.mockReset();
    mockCouchdbService.createDocument.mockReset();
    mockCouchdbService.updateDocument.mockReset();
    mockCouchdbService.findByType.mockReset();
    mockCouchdbService.find.mockReset();
    mockCouchdbService.deleteDocument.mockReset();
    mockCouchdbService.updatePost.mockReset();
  });

  describe('Schema Validation', () => {
    it('should create a valid comment', async () => {
      const commentData = {
        user: 'user_123',
        post: 'post_123',
        content: 'This is a great post!',
      };

      const mockUser = {
        _id: 'user_123',
        name: 'Test User',
        profilePicture: ''
      };

      const mockPost = {
        _id: 'post_123',
        content: 'Test post content',
        user: { userId: 'user_123' },
        commentsCount: 0
      };

      const mockCreated = {
        _id: 'comment_123',
        _rev: '1-abc',
        type: 'comment',
        user: {
          userId: 'user_123',
          name: 'Test User',
          profilePicture: ''
        },
        post: {
          postId: 'post_123',
          content: 'Test post content',
          userId: 'user_123'
        },
        content: 'This is a great post!',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.findUserById.mockResolvedValue(mockUser);
      mockCouchdbService.getById.mockResolvedValue(mockPost);
      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);
      mockCouchdbService.updatePost.mockResolvedValue(mockPost);

      const comment = await Comment.create(commentData);

      expect(comment._id).toBeDefined();
      expect(comment.user.userId).toBe('user_123');
      expect(comment.post.postId).toBe('post_123');
      expect(comment.content).toBe('This is a great post!');
    });

    it('should require post field', async () => {
      const commentData = {
        user: { userId: 'user_123' },
        content: 'Comment without post',
      };

      expect(() => new Comment(commentData)).toThrow();
    });

    it('should require user field', async () => {
      const commentData = {
        post: { postId: 'post_123' },
        content: 'Comment without user',
      };

      expect(() => new Comment(commentData)).toThrow();
    });

    it('should require content field', async () => {
      const commentData = {
        post: { postId: 'post_123' },
        user: { userId: 'user_123' },
      };

      expect(() => new Comment(commentData)).toThrow();
    });
  });

  describe('Content Validation', () => {
    it('should trim content', async () => {
      const commentData = {
        post: 'post_123',
        user: 'user_123',
        content: '  This comment has spaces  ',
      };

      const mockUser = { _id: 'user_123', name: 'Test User' };
      const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } };

      const mockCreated = {
        _id: 'comment_123',
        _rev: '1-abc',
        type: 'comment',
        user: { userId: 'user_123', name: 'Test User' },
        post: { postId: 'post_123', content: 'Test post', userId: 'user_123' },
        content: 'This comment has spaces',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.findUserById.mockResolvedValue(mockUser);
      mockCouchdbService.getById.mockResolvedValue(mockPost);
      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);
      mockCouchdbService.updatePost.mockResolvedValue(mockPost);

      const comment = await Comment.create(commentData);

      expect(comment.content).toBe('This comment has spaces');
    });

    it('should allow long comments', async () => {
      const longContent = 'a'.repeat(400); // Within 500 character limit

      const commentData = {
        post: 'post_123',
        user: 'user_123',
        content: longContent,
      };

      const mockUser = { _id: 'user_123', name: 'Test User' };
      const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } };

      const mockCreated = {
        _id: 'comment_123',
        _rev: '1-abc',
        type: 'comment',
        user: { userId: 'user_123', name: 'Test User' },
        post: { postId: 'post_123', content: 'Test post', userId: 'user_123' },
        content: longContent,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.findUserById.mockResolvedValue(mockUser);
      mockCouchdbService.getById.mockResolvedValue(mockPost);
      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);
      mockCouchdbService.updatePost.mockResolvedValue(mockPost);

      const comment = await Comment.create(commentData);

      expect(comment.content).toBe(longContent);
    });

    it('should reject empty content after trimming', async () => {
      const commentData = {
        post: { postId: 'post_123' },
        user: { userId: 'user_123' },
        content: '   ', // Only spaces
      };

      expect(() => new Comment(commentData)).toThrow();
    });
  });

  describe('Basic Properties', () => {
    it('should create comment with basic properties', async () => {
      const commentData = {
        post: 'post_123',
        user: 'user_123',
        content: 'Basic comment',
      };

      const mockUser = { _id: 'user_123', name: 'Test User' };
      const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } };

      const mockCreated = {
        _id: 'comment_123',
        _rev: '1-abc',
        type: 'comment',
        user: { userId: 'user_123', name: 'Test User' },
        post: { postId: 'post_123', content: 'Test post', userId: 'user_123' },
        content: 'Basic comment',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.findUserById.mockResolvedValue(mockUser);
      mockCouchdbService.getById.mockResolvedValue(mockPost);
      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);
      mockCouchdbService.updatePost.mockResolvedValue(mockPost);

      const comment = await Comment.create(commentData);

      expect(comment._id).toBeDefined();
      expect(comment.type).toBe('comment');
      expect(comment.content).toBe('Basic comment');
    });
  });

  describe('Relationships', () => {
    it('should reference post ID', async () => {
      const commentData = {
        post: 'post_123',
        user: 'user_123',
        content: 'Comment on specific post',
      };

      const mockUser = { _id: 'user_123', name: 'Test User' };
      const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } };

      const mockCreated = {
        _id: 'comment_123',
        _rev: '1-abc',
        type: 'comment',
        user: { userId: 'user_123', name: 'Test User' },
        post: { postId: 'post_123', content: 'Test post', userId: 'user_123' },
        content: 'Comment on specific post',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.findUserById.mockResolvedValue(mockUser);
      mockCouchdbService.getById.mockResolvedValue(mockPost);
      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);
      mockCouchdbService.updatePost.mockResolvedValue(mockPost);

      const comment = await Comment.create(commentData);

      expect(comment.post.postId).toBe('post_123');
    });

    it('should reference user ID', async () => {
      const commentData = {
        post: 'post_123',
        user: 'user_123',
        content: 'Comment by specific user',
      };

      const mockUser = { _id: 'user_123', name: 'Test User' };
      const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } };

      const mockCreated = {
        _id: 'comment_123',
        _rev: '1-abc',
        type: 'comment',
        user: { userId: 'user_123', name: 'Test User' },
        post: { postId: 'post_123', content: 'Test post', userId: 'user_123' },
        content: 'Comment by specific user',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.findUserById.mockResolvedValue(mockUser);
      mockCouchdbService.getById.mockResolvedValue(mockPost);
      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);
      mockCouchdbService.updatePost.mockResolvedValue(mockPost);

      const comment = await Comment.create(commentData);

      expect(comment.user.userId).toBe('user_123');
    });
  });

  describe('Timestamps', () => {
    it('should automatically set createdAt and updatedAt', async () => {
      const commentData = {
        post: 'post_123',
        user: 'user_123',
        content: 'Timestamp test comment',
      };

      const mockUser = { _id: 'user_123', name: 'Test User' };
      const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } };

      const mockCreated = {
        _id: 'comment_123',
        _rev: '1-abc',
        type: 'comment',
        user: { userId: 'user_123', name: 'Test User' },
        post: { postId: 'post_123', content: 'Test post', userId: 'user_123' },
        content: 'Timestamp test comment',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.findUserById.mockResolvedValue(mockUser);
      mockCouchdbService.getById.mockResolvedValue(mockPost);
      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);
      mockCouchdbService.updatePost.mockResolvedValue(mockPost);

      const comment = await Comment.create(commentData);

      expect(comment.createdAt).toBeDefined();
      expect(comment.updatedAt).toBeDefined();
      expect(typeof comment.createdAt).toBe('string');
      expect(typeof comment.updatedAt).toBe('string');
    });

    it('should update updatedAt on modification', async () => {
      const mockComment = {
        _id: 'comment_123',
        _rev: '1-abc',
        type: 'comment',
        user: { userId: 'user_123', name: 'Test User' },
        post: { postId: 'post_123', content: 'Test post', userId: 'user_123' },
        content: 'Update test comment',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.getById.mockResolvedValue(mockComment);
      mockCouchdbService.updateDocument.mockResolvedValue({
        ...mockComment,
        content: 'Updated comment content',
        _rev: '2-def',
        updatedAt: '2023-01-01T00:00:01.000Z'
      });

      const comment = await Comment.findById('comment_123');
      const originalUpdatedAt = comment.updatedAt;

      comment.content = 'Updated comment content';
      await comment.save();

      expect(comment.updatedAt).not.toBe(originalUpdatedAt);
    });
  });

  describe('Content Edge Cases', () => {
    it('should handle special characters in content', async () => {
      const commentData = {
        post: 'post_123',
        user: 'user_123',
        content: 'This comment has émojis 🎉 and spëcial charactërs!',
      };

      const mockUser = { _id: 'user_123', name: 'Test User' };
      const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } };

      const mockCreated = {
        _id: 'comment_123',
        _rev: '1-abc',
        type: 'comment',
        user: { userId: 'user_123', name: 'Test User' },
        post: { postId: 'post_123', content: 'Test post', userId: 'user_123' },
        content: 'This comment has émojis 🎉 and spëcial charactërs!',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.findUserById.mockResolvedValue(mockUser);
      mockCouchdbService.getById.mockResolvedValue(mockPost);
      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);
      mockCouchdbService.updatePost.mockResolvedValue(mockPost);

      const comment = await Comment.create(commentData);

      expect(comment.content).toBe('This comment has émojis 🎉 and spëcial charactërs!');
    });

    it('should handle newlines in content', async () => {
      const commentData = {
        post: 'post_123',
        user: 'user_123',
        content: 'This comment\nhas\nmultiple\nlines',
      };

      const mockUser = { _id: 'user_123', name: 'Test User' };
      const mockPost = { _id: 'post_123', content: 'Test post', user: { userId: 'user_123' } };

      const mockCreated = {
        _id: 'comment_123',
        _rev: '1-abc',
        type: 'comment',
        user: { userId: 'user_123', name: 'Test User' },
        post: { postId: 'post_123', content: 'Test post', userId: 'user_123' },
        content: 'This comment\nhas\nmultiple\nlines',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.findUserById.mockResolvedValue(mockUser);
      mockCouchdbService.getById.mockResolvedValue(mockPost);
      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);
      mockCouchdbService.updatePost.mockResolvedValue(mockPost);

      const comment = await Comment.create(commentData);

      expect(comment.content).toBe('This comment\nhas\nmultiple\nlines');
    });
  });

  describe('Static Methods', () => {
    it('should find comment by ID', async () => {
      const mockComment = {
        _id: 'comment_123',
        _rev: '1-abc',
        type: 'comment',
        user: { userId: 'user_123', name: 'Test User' },
        post: { postId: 'post_123', content: 'Test post', userId: 'user_123' },
        content: 'Test comment',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.getById.mockResolvedValue(mockComment);

      const comment = await Comment.findById('comment_123');
      expect(comment).toBeDefined();
      expect(comment._id).toBe('comment_123');
      expect(comment.content).toBe('Test comment');
    });

    it('should return null when comment not found', async () => {
      mockCouchdbService.getById.mockResolvedValue(null);

      const comment = await Comment.findById('nonexistent');
      expect(comment).toBeNull();
    });
  });
});
