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

// Mock dependencies
jest.mock('../../../models', () => ({
  Feedback: {
    create: jest.fn(),
  },
  User: {
    findByPk: jest.fn(),
  },
}));

jest.mock('../../../middleware/auth', () => ({
  authenticateToken: (req, res, next) => {
    req.user = { id: 'user-123', email: 'test@example.com' };
    next();
  },
}));

jest.mock('../../../middleware/validation', () => ({
  validateFeedback: (req, res, next) => next(),
  sanitizeInput: (req, res, next) => next(),
}));

jest.mock('../../../services/email', () => ({
  feedback: {
    sendFeedbackConfirmation: jest.fn(),
    sendFeedbackNotificationToAdmin: jest.fn(),
  },
}));

jest.mock('../../../utils/logger', () => ({
  info: jest.fn(),
  error: jest.fn(),
  warn: jest.fn(),
  withRequestId: jest.fn(() => ({
    info: jest.fn(),
    error: jest.fn(),
    warn: jest.fn(),
  })),
}));

const { Feedback } = require('../../../models');
const emailServices = require('../../../services/email');
const feedbackRoutes = require('../../../routes/feedback');

describe('Feedback Routes', () => {
  let app;

  beforeEach(() => {
    app = express();
    app.use(express.json());
    app.use('/feedback', feedbackRoutes);

    // Add error handler
    app.use((err, req, res, next) => {
      res.status(500).json({ error: err.message });
    });

    jest.clearAllMocks();
  });

  describe('POST /feedback', () => {
    it('should create feedback successfully', async () => {
      const mockFeedback = {
        id: 'feedback-123',
        userId: 'user-123',
        feedbackText: 'Great app!',
        url: 'https://example.com/page',
        userAgent: 'Mozilla/5.0',
      };

      Feedback.create.mockResolvedValue(mockFeedback);
      emailServices.feedback.sendFeedbackConfirmation.mockResolvedValue();
      emailServices.feedback.sendFeedbackNotificationToAdmin.mockResolvedValue();

      const response = await request(app)
        .post('/feedback')
        .set('User-Agent', 'Mozilla/5.0')
        .send({
          feedbackText: 'Great app!',
          url: 'https://example.com/page',
        });

      expect(response.status).toBe(201);
      expect(response.body.id).toBe('feedback-123');
      expect(response.body.feedbackText).toBe('Great app!');
      expect(Feedback.create).toHaveBeenCalledWith({
        userId: 'user-123',
        feedbackText: 'Great app!',
        url: 'https://example.com/page',
        userAgent: 'Mozilla/5.0',
      });
    });

    it('should send confirmation email to user', async () => {
      const mockFeedback = {
        id: 'feedback-123',
        feedbackText: 'Great app!',
      };

      Feedback.create.mockResolvedValue(mockFeedback);
      emailServices.feedback.sendFeedbackConfirmation.mockResolvedValue();
      emailServices.feedback.sendFeedbackNotificationToAdmin.mockResolvedValue();

      await request(app)
        .post('/feedback')
        .send({ feedbackText: 'Great app!' });

      expect(emailServices.feedback.sendFeedbackConfirmation).toHaveBeenCalledWith(
        { id: 'user-123', email: 'test@example.com' },
        mockFeedback
      );
    });

    it('should send notification email to admin', async () => {
      const mockFeedback = {
        id: 'feedback-123',
        feedbackText: 'Great app!',
      };

      Feedback.create.mockResolvedValue(mockFeedback);
      emailServices.feedback.sendFeedbackConfirmation.mockResolvedValue();
      emailServices.feedback.sendFeedbackNotificationToAdmin.mockResolvedValue();

      await request(app)
        .post('/feedback')
        .send({ feedbackText: 'Great app!' });

      expect(emailServices.feedback.sendFeedbackNotificationToAdmin).toHaveBeenCalledWith(
        { id: 'user-123', email: 'test@example.com' },
        mockFeedback
      );
    });

    it('should succeed even if confirmation email fails', async () => {
      const mockFeedback = {
        id: 'feedback-123',
        feedbackText: 'Great app!',
      };

      Feedback.create.mockResolvedValue(mockFeedback);
      emailServices.feedback.sendFeedbackConfirmation.mockRejectedValue(new Error('Email failed'));
      emailServices.feedback.sendFeedbackNotificationToAdmin.mockResolvedValue();

      const response = await request(app)
        .post('/feedback')
        .send({ feedbackText: 'Great app!' });

      expect(response.status).toBe(201);
    });

    it('should succeed even if admin notification email fails', async () => {
      const mockFeedback = {
        id: 'feedback-123',
        feedbackText: 'Great app!',
      };

      Feedback.create.mockResolvedValue(mockFeedback);
      emailServices.feedback.sendFeedbackConfirmation.mockResolvedValue();
      emailServices.feedback.sendFeedbackNotificationToAdmin.mockRejectedValue(new Error('Email failed'));

      const response = await request(app)
        .post('/feedback')
        .send({ feedbackText: 'Great app!' });

      expect(response.status).toBe(201);
    });

    it('should handle feedback with null url', async () => {
      const mockFeedback = {
        id: 'feedback-123',
        feedbackText: 'Great app!',
        url: null,
      };

      Feedback.create.mockResolvedValue(mockFeedback);
      emailServices.feedback.sendFeedbackConfirmation.mockResolvedValue();
      emailServices.feedback.sendFeedbackNotificationToAdmin.mockResolvedValue();

      const response = await request(app)
        .post('/feedback')
        .send({ feedbackText: 'Great app!' });

      expect(response.status).toBe(201);
      expect(Feedback.create).toHaveBeenCalledWith(
        expect.objectContaining({
          url: null,
        })
      );
    });

    it('should capture user agent from headers', async () => {
      const mockFeedback = {
        id: 'feedback-123',
        feedbackText: 'Great app!',
        userAgent: 'CustomUserAgent/1.0',
      };

      Feedback.create.mockResolvedValue(mockFeedback);
      emailServices.feedback.sendFeedbackConfirmation.mockResolvedValue();
      emailServices.feedback.sendFeedbackNotificationToAdmin.mockResolvedValue();

      await request(app)
        .post('/feedback')
        .set('User-Agent', 'CustomUserAgent/1.0')
        .send({ feedbackText: 'Great app!' });

      expect(Feedback.create).toHaveBeenCalledWith(
        expect.objectContaining({
          userAgent: 'CustomUserAgent/1.0',
        })
      );
    });

    it('should handle missing user agent', async () => {
      const mockFeedback = {
        id: 'feedback-123',
        feedbackText: 'Great app!',
      };

      Feedback.create.mockResolvedValue(mockFeedback);
      emailServices.feedback.sendFeedbackConfirmation.mockResolvedValue();
      emailServices.feedback.sendFeedbackNotificationToAdmin.mockResolvedValue();

      const response = await request(app)
        .post('/feedback')
        .send({ feedbackText: 'Great app!' });

      expect(response.status).toBe(201);
    });

    it('should return 500 when database error occurs', async () => {
      Feedback.create.mockRejectedValue(new Error('Database error'));

      const response = await request(app)
        .post('/feedback')
        .send({ feedbackText: 'Great app!' });

      expect(response.status).toBe(500);
    });
  });
});
