const request = require('supertest');
const express = require('express');

// Mock Post model before importing routes
jest.mock('../../models/Post', () => {
  const MockPost = jest.fn().mockImplementation((data) => ({
    _id: data._id || `post_${Date.now()}`,
    content: data.content,
    imageUrl: data.imageUrl,
    cloudinaryPublicId: data.cloudinaryPublicId,
    user: data.user,
    likes: data.likes || [],
    comments: data.comments || [],
    createdAt: data.createdAt || new Date(),
    updatedAt: data.updatedAt || new Date(),
    save: jest.fn().mockResolvedValue(this),
    populate: jest.fn().mockResolvedValue(this),
    toJSON: jest.fn().mockReturnValue(this),
    ...data
  }));

  MockPost.findById = jest.fn();
  MockPost.find = jest.fn();
  MockPost.findAll = jest.fn();
  MockPost.countDocuments = jest.fn();
  MockPost.create = jest.fn();
  MockPost.updatePost = jest.fn();
  MockPost.addLike = jest.fn();
  MockPost.removeLike = jest.fn();

  return MockPost;
});

const postRoutes = require('../../routes/posts');
const { createTestUser, createTestPost } = require('../utils/testHelpers');
const couchdbService = require('../../services/couchdbService');
const Post = require('../../models/Post');

const app = express();
app.use(express.json());
app.use('/api/posts', postRoutes);

describe('Post Routes', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    
    // Reset default implementations
    Post.findById.mockResolvedValue(null);
    Post.find.mockResolvedValue([]);
    Post.findAll.mockResolvedValue([]);
    Post.countDocuments.mockResolvedValue(0);
    Post.create.mockImplementation((data) => {
      // Return post structure matching actual Post model (user object with userId)
      return Promise.resolve({
        _id: '507f1f77bcf86cd799439011',
        user: {
          userId: data.user || '507f1f77bcf86cd799439099',
          name: 'Test User',
          profilePicture: ''
        },
        content: data.content,
        imageUrl: data.imageUrl,
        cloudinaryPublicId: data.cloudinaryPublicId,
        likes: [],
        likesCount: 0,
        commentsCount: 0,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
      });
    });
    Post.updatePost.mockImplementation((id, data) => Promise.resolve({
      _id: id,
      ...data
    }));
    Post.addLike.mockImplementation((id, userId) => Promise.resolve({
      _id: id,
      likes: [userId]
    }));
    Post.removeLike.mockImplementation((id, userId) => Promise.resolve({
      _id: id,
      likes: []
    }));
  });

  describe('GET /api/posts', () => {
    it('should get all posts with pagination', async () => {
      const { user } = await createTestUser();
      const post1 = await createTestPost(user._id, { content: 'First post' });
      const post2 = await createTestPost(user._id, { content: 'Second post' });

      const mockPosts = [post1, post2];
      Post.findAll.mockResolvedValue(mockPosts);
      Post.countDocuments.mockResolvedValue(2);

      const response = await request(app)
        .get('/api/posts')
        .expect(200);

      expect(response.body).toHaveProperty('data');
      expect(response.body).toHaveProperty('pagination');
      expect(Array.isArray(response.body.data)).toBe(true);
      expect(response.body.data.length).toBe(2);
    });

    it('should return empty array when no posts exist', async () => {
      Post.findAll.mockResolvedValue([]);
      Post.countDocuments.mockResolvedValue(0);

      const response = await request(app)
        .get('/api/posts')
        .expect(200);

      expect(response.body).toHaveProperty('data');
      expect(response.body.data.length).toBe(0);
    });
  });

  describe('POST /api/posts', () => {
    it('should create a new post with authentication', async () => {
      const { user, token } = await createTestUser();

      const postData = {
        content: 'This is my new post about street cleaning',
      };

      const mockPost = {
        _id: '507f1f77bcf86cd799439011',
        user: {
          userId: user._id,
          name: user.name,
          profilePicture: user.profilePicture || ''
        },
        content: postData.content,
        likes: [],
        likesCount: 0,
        commentsCount: 0,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
      };

      Post.create.mockResolvedValue(mockPost);

      const response = await request(app)
        .post('/api/posts')
        .set('x-auth-token', token)
        .send(postData);
      
      console.log('Response status:', response.status);
      console.log('Response body:', JSON.stringify(response.body, null, 2));
      
      if (response.status !== 200) {
        console.log('ERROR - Status:', response.status);
        console.log('ERROR - Body:', JSON.stringify(response.body, null, 2));
      }
      expect(response.status).toBe(200);
      expect(response.body).toHaveProperty('post');
      expect(response.body.post).toHaveProperty('content', postData.content);
      expect(response.body.post.user).toHaveProperty('userId', user._id);
      expect(response.body).toHaveProperty('pointsAwarded', 5);
    });

    it('should create a post with image', async () => {
      const { user, token } = await createTestUser();

      const postData = {
        content: 'Post with image',
        imageUrl: 'https://example.com/image.jpg',
        cloudinaryPublicId: 'test_public_id',
      };

      const mockPost = {
        _id: '507f1f77bcf86cd799439012',
        user: {
          userId: user._id,
          name: user.name,
          profilePicture: user.profilePicture || ''
        },
        content: postData.content,
        imageUrl: postData.imageUrl,
        cloudinaryPublicId: postData.cloudinaryPublicId,
        likes: [],
        likesCount: 0,
        commentsCount: 0,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
      };

      Post.create.mockResolvedValue(mockPost);

      const response = await request(app)
        .post('/api/posts')
        .set('x-auth-token', token)
        .send(postData)
        .expect(200);

      expect(response.body.post).toHaveProperty('imageUrl', postData.imageUrl);
      expect(response.body.post).toHaveProperty('cloudinaryPublicId', postData.cloudinaryPublicId);
    });

    it('should not create post without authentication', async () => {
      const postData = {
        content: 'This should fail',
      };

      const response = await request(app)
        .post('/api/posts')
        .send(postData)
        .expect(401);

      expect(response.body).toHaveProperty('msg', 'No token, authorization denied');
    });

    it('should not create post without content', async () => {
      const { token } = await createTestUser();

      const response = await request(app)
        .post('/api/posts')
        .set('x-auth-token', token)
        .send({})
        .expect(400);

      expect(response.body).toHaveProperty('msg', 'Content is required');
    });
  });

  describe('PUT /api/posts/like/:id', () => {
    it('should like a post', async () => {
      const { user: author } = await createTestUser({ email: 'author@example.com' });
      const { user: liker, token } = await createTestUser({ email: 'liker@example.com' });
      const postId = '507f1f77bcf86cd799439011';

      const post = {
        _id: postId,
        user: {
          userId: author._id,
          name: author.name,
          profilePicture: author.profilePicture || ''
        },
        likes: [],
        likesCount: 0,
        commentsCount: 0,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
      };

      const mockPost = {
        ...post,
        likes: [liker._id],
        likesCount: 1
      };

      Post.findById.mockResolvedValue(post);
      Post.addLike.mockResolvedValue(mockPost);

      const response = await request(app)
        .put(`/api/posts/like/${postId}`)
        .set('x-auth-token', token)
        .expect(200);

      expect(Array.isArray(response.body)).toBe(true);
      expect(response.body.length).toBe(1);
      expect(response.body[0]).toBe(liker._id);
    });

    it('should not like a post twice', async () => {
      const { user: author } = await createTestUser({ email: 'author@example.com' });
      const { user: liker, token } = await createTestUser({ email: 'liker@example.com' });
      const postId = '507f1f77bcf86cd799439011';

      const mockPost = {
        _id: postId,
        user: {
          userId: author._id,
          name: author.name,
          profilePicture: author.profilePicture || ''
        },
        likes: [liker._id],
        likesCount: 1,
        commentsCount: 0,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
      };

      Post.findById.mockResolvedValue(mockPost);

      const response = await request(app)
        .put(`/api/posts/like/${postId}`)
        .set('x-auth-token', token)
        .expect(400);

      expect(response.body).toHaveProperty('msg', 'Post already liked');
    });

    it('should return 404 for non-existent post', async () => {
      const { token } = await createTestUser();
      const fakeId = '507f1f77bcf86cd799439011';

      Post.findById.mockResolvedValue(null);

      const response = await request(app)
        .put(`/api/posts/like/${fakeId}`)
        .set('x-auth-token', token)
        .expect(404);

      expect(response.body).toHaveProperty('msg', 'Post not found');
    });

    it('should not like post without authentication', async () => {
      const { user } = await createTestUser();
      const postId = '507f1f77bcf86cd799439011';

      const response = await request(app)
        .put(`/api/posts/like/${postId}`)
        .expect(401);

      expect(response.body).toHaveProperty('msg', 'No token, authorization denied');
    });
  });

  describe('PUT /api/posts/unlike/:id', () => {
    it('should unlike a post', async () => {
      const { user: author } = await createTestUser({ email: 'author@example.com' });
      const { user: liker, token } = await createTestUser({ email: 'liker@example.com' });
      const postId = '507f1f77bcf86cd799439011';

      const mockPost = {
        _id: postId,
        user: {
          userId: author._id,
          name: author.name,
          profilePicture: author.profilePicture || ''
        },
        likes: [liker._id],
        likesCount: 1,
        commentsCount: 0,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
      };

      const mockUpdatedPost = {
        ...mockPost,
        likes: [],
        likesCount: 0
      };

      Post.findById.mockResolvedValue(mockPost);
      Post.removeLike.mockResolvedValue(mockUpdatedPost);

      const response = await request(app)
        .put(`/api/posts/unlike/${postId}`)
        .set('x-auth-token', token)
        .expect(200);

      expect(Array.isArray(response.body)).toBe(true);
      expect(response.body.length).toBe(0);
    });

    it('should not unlike a post that was not liked', async () => {
      const { user: author } = await createTestUser({ email: 'author@example.com' });
      const { user: liker, token } = await createTestUser({ email: 'liker@example.com' });
      const postId = '507f1f77bcf86cd799439011';

      const post = {
        _id: postId,
        user: {
          userId: author._id,
          name: author.name,
          profilePicture: author.profilePicture || ''
        },
        likes: [],
        likesCount: 0,
        commentsCount: 0,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
      };

      Post.findById.mockResolvedValue(post);

      const response = await request(app)
        .put(`/api/posts/unlike/${postId}`)
        .set('x-auth-token', token)
        .expect(400);

      expect(response.body).toHaveProperty('msg', 'Post not yet liked');
    });
  });
});