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

// Mock CouchDB service before importing routes
jest.mock('../../services/couchdbService', () => ({
  initialize: jest.fn().mockResolvedValue(true),
  create: jest.fn(),
  getById: jest.fn(),
  find: jest.fn(),
  createDocument: jest.fn().mockImplementation((doc) => Promise.resolve({
    _id: `event_${Date.now()}`,
    _rev: '1-test',
    type: 'event',
    ...doc
  })),
  updateDocument: jest.fn().mockImplementation((id, doc) => Promise.resolve({
    _id: id,
    _rev: '2-test',
    ...doc
  })),
  deleteDocument: jest.fn(),
  findByType: jest.fn().mockResolvedValue([]),
  findUserById: jest.fn(),
  findUserByEmail: jest.fn(),
  update: jest.fn(),
  getDocument: jest.fn(),
  updateUserPoints: jest.fn().mockResolvedValue({
    _id: 'user_123',
    points: 15
  }),
  checkAndAwardBadges: jest.fn().mockResolvedValue([])
}));

const eventsRoutes = require('../../routes/events');
const Event = require('../../models/Event');
const User = require('../../models/User');
const { createTestUser, createTestEvent } = require('../utils/testHelpers');
const couchdbService = require('../../services/couchdbService');

// Create Express app for testing
const app = express();
app.use(express.json());
app.use('/api/events', eventsRoutes);

describe('Events Routes', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    
    // Mock Event model methods
    Event.getAllPaginated = jest.fn().mockResolvedValue({
      events: [],
      pagination: { totalCount: 0 }
    });
    Event.findById = jest.fn().mockResolvedValue(null);
    Event.create = jest.fn().mockImplementation((data) => Promise.resolve({
      _id: `event_${Date.now()}`,
      _rev: '1-test',
      type: 'event',
      ...data
    }));
    Event.addParticipant = jest.fn().mockResolvedValue({
      _id: 'event_123',
      participants: []
    });
    
    // Mock User model methods
    User.findById = jest.fn().mockResolvedValue({
      _id: 'user_123',
      name: 'Test User',
      profilePicture: '',
      events: [],
      stats: {
        streetsAdopted: 0,
        tasksCompleted: 0,
        postsCreated: 0,
        eventsParticipated: 0,
        badgesEarned: 0
      }
    });
    User.update = jest.fn().mockResolvedValue(true);
    
    // Mock couchdbService methods
    couchdbService.find.mockResolvedValue([]);
    couchdbService.getDocument.mockResolvedValue(null);
  });
  describe('GET /api/events', () => {
    it('should get all events', async () => {
      const { user } = await createTestUser();
      const event1 = await createTestEvent(user.id, { title: 'Event 1' });
      const event2 = await createTestEvent(user.id, { title: 'Event 2' });

      // Mock Event.getAllPaginated to return test events
      Event.getAllPaginated.mockResolvedValue({
        events: [event1, event2],
        pagination: { totalCount: 2 }
      });

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

      expect(Array.isArray(response.body.data)).toBe(true);
      expect(response.body.data.length).toBe(2);
      expect(response.body.data[0]).toHaveProperty('title');
    });

    it('should return empty array when no events exist', async () => {
      // Mock Event.getAllPaginated to return empty array
      Event.getAllPaginated.mockResolvedValue({
        events: [],
        pagination: { totalCount: 0 }
      });

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

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

  describe('POST /api/events', () => {
    it('should create a new event with authentication', async () => {
      const { token } = await createTestUser();
      const eventData = {
        title: 'Community Cleanup',
        description: 'Annual community cleanup event',
        date: new Date(Date.now() + 86400000),
        location: 'Central Park',
      };

      // Mock Event.create to return created event
      const createdEvent = {
        _id: 'event_123',
        _rev: '1-test',
        type: 'event',
        ...eventData,
        participants: [],
        participantsCount: 0,
        status: 'upcoming',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };
      Event.create.mockResolvedValue(createdEvent);

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

      expect(response.body).toHaveProperty('_id');
      expect(response.body.title).toBe(eventData.title);
      expect(response.body.description).toBe(eventData.description);
      expect(response.body.location).toBe(eventData.location);
    });

    it('should reject event creation without authentication', async () => {
      const eventData = {
        title: 'Unauthorized Event',
        description: 'This should fail',
        date: new Date(Date.now() + 86400000),
        location: 'Nowhere',
      };

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

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

    it('should handle missing required fields', async () => {
      const { token } = await createTestUser();

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

      expect(response.body).toHaveProperty('success', false);
    });
  });

  describe('PUT /api/events/rsvp/:id', () => {
    it('should allow user to RSVP to an event', async () => {
      const { user, token } = await createTestUser();
      const event = await createTestEvent(user.id);

      // Mock Event.findById to return test event
      Event.findById.mockResolvedValue(event);
      
      // Mock User.findById to return test user
      User.findById.mockResolvedValue({
        _id: user.id,
        name: user.name,
        profilePicture: '',
        events: [],
        stats: {
          streetsAdopted: 0,
          tasksCompleted: 0,
          postsCreated: 0,
          eventsParticipated: 0,
          badgesEarned: 0
        }
      });
      
      // Mock Event.addParticipant to return updated event
      const updatedEvent = {
        ...event,
        participants: [{
          userId: user.id,
          name: user.name,
          profilePicture: ''
        }]
      };
      Event.addParticipant.mockResolvedValue(updatedEvent);

      const response = await request(app)
        .put(`/api/events/rsvp/${event.id}`)
        .set('x-auth-token', token)
        .expect(200);

      expect(response.body).toHaveProperty('participants');
      expect(response.body).toHaveProperty('pointsAwarded', 15);
      expect(response.body).toHaveProperty('newBalance', 15);
    });

    it('should not allow duplicate RSVPs', async () => {
      const { user, token } = await createTestUser();
      
      // Create event with user already participating
      const event = await createTestEvent(user._id);
      event.participants = [{
        userId: user._id,  // Use _id to match what JWT contains
        name: user.name,
        profilePicture: ''
      }];

      // Mock Event.findById to return event with user already participating
      Event.findById.mockResolvedValue(event);

      const response = await request(app)
        .put(`/api/events/rsvp/${event.id}`)
        .set('x-auth-token', token)
        .expect(400);

      expect(response.body).toHaveProperty('msg', 'Already RSVPed');
    });

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

      // Mock Event.findById to return null (not found)
      Event.findById.mockResolvedValue(null);

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

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

    it('should reject RSVP without authentication', async () => {
      const { user } = await createTestUser();
      const event = await createTestEvent(user.id);

      const response = await request(app)
        .put(`/api/events/rsvp/${event.id}`)
        .expect(401);

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

    it('should handle invalid event ID format', async () => {
      const { token } = await createTestUser();

      const response = await request(app)
        .put('/api/events/rsvp/invalid-id')
        .set('x-auth-token', token)
        .expect(400);

      expect(response.body).toHaveProperty('success', false);
      expect(response.body.errors).toBeDefined();
    });
  });
});
