// Mock dependencies BEFORE requiring modules
jest.mock('../../../models', () => ({
  Rental: {
    findByPk: jest.fn()
  },
  Item: jest.fn(),
  User: {
    findByPk: jest.fn()
  }
}));
jest.mock('../../../services/email', () => ({
  customerService: {
    sendLateReturnToCustomerService: jest.fn().mockResolvedValue()
  }
}));
jest.mock('../../../config/aws', () => ({
  getAWSConfig: jest.fn(() => ({ region: 'us-east-1' })),
  getAWSCredentials: jest.fn()
}));

const LateReturnService = require('../../../services/lateReturnService');
const { Rental, Item, User } = require('../../../models');
const emailService = require('../../../services/email');

describe('LateReturnService', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('calculateLateFee', () => {
    it('should return no late fee when returned on time or early', () => {
      const rental = {
        endDateTime: new Date('2023-06-01T10:00:00Z'),
        item: { pricePerHour: 10, pricePerDay: 50 }
      };
      const actualReturn = new Date('2023-06-01T09:30:00Z'); // 30 minutes early

      const result = LateReturnService.calculateLateFee(rental, actualReturn);

      expect(result.isLate).toBe(false);
      expect(result.lateFee).toBe(0);
      expect(result.lateHours).toBe(0);
    });

    it('should calculate late fee using daily rate when available', () => {
      const rental = {
        endDateTime: new Date('2023-06-01T10:00:00Z'),
        item: { pricePerHour: 10, pricePerDay: 50 }
      };
      const actualReturn = new Date('2023-06-01T14:00:00Z'); // 4 hours late = 1 billable day

      const result = LateReturnService.calculateLateFee(rental, actualReturn);

      expect(result.isLate).toBe(true);
      expect(result.lateFee).toBe(50); // 1 billable day * $50 daily rate
      expect(result.lateHours).toBe(4);
      expect(result.pricingType).toBe('daily');
    });

    it('should calculate late fee using daily rate when no hourly rate', () => {
      const rental = {
        endDateTime: new Date('2023-06-01T10:00:00Z'),
        item: { pricePerDay: 100 }
      };
      const actualReturn = new Date('2023-06-02T14:00:00Z'); // 28 hours late (2 days rounded up)

      const result = LateReturnService.calculateLateFee(rental, actualReturn);

      expect(result.isLate).toBe(true);
      expect(result.lateFee).toBe(200); // 2 days * $100
      expect(result.pricingType).toBe('daily');
    });

    it('should use free borrow fallback rates when item has no pricing', () => {
      const rental = {
        startDateTime: new Date('2023-06-01T08:00:00Z'),
        endDateTime: new Date('2023-06-01T10:00:00Z'), // 2 hour rental
        item: {}
      };
      const actualReturn = new Date('2023-06-01T14:00:00Z'); // 4 hours late = 1 billable day

      const result = LateReturnService.calculateLateFee(rental, actualReturn);

      expect(result.isLate).toBe(true);
      expect(result.lateFee).toBe(10); // 1 billable day * $10 (free borrow daily rate)
      expect(result.pricingType).toBe('daily');
    });
  });

  describe('processLateReturn', () => {
    let mockRental;

    beforeEach(() => {
      // Active status is computed: confirmed + startDateTime in the past
      mockRental = {
        id: '123',
        status: 'confirmed',
        startDateTime: new Date('2023-05-01T08:00:00Z'), // In the past
        endDateTime: new Date('2023-06-01T10:00:00Z'),
        item: { pricePerHour: 10, name: 'Test Item' },
        renterId: 'renter-123',
        update: jest.fn()
      };

      Rental.findByPk.mockResolvedValue(mockRental);
    });

    it('should process late return and send email to customer service', async () => {
      const actualReturn = new Date('2023-06-01T14:00:00Z'); // 4 hours late = 1 billable day

      const mockOwner = { id: 'owner-456', email: 'owner@test.com' };
      const mockRenter = { id: 'renter-123', email: 'renter@test.com' };

      // Mock User.findByPk for owner and renter
      User.findByPk
        .mockResolvedValueOnce(mockOwner)  // First call for owner
        .mockResolvedValueOnce(mockRenter); // Second call for renter

      mockRental.update.mockResolvedValue({
        ...mockRental,
        status: 'returned_late',
        actualReturnDateTime: actualReturn,
        payoutStatus: 'pending'
      });

      const result = await LateReturnService.processLateReturn('123', actualReturn);

      expect(mockRental.update).toHaveBeenCalledWith({
        actualReturnDateTime: actualReturn,
        status: 'returned_late',
        payoutStatus: 'pending'
      });

      expect(emailService.customerService.sendLateReturnToCustomerService).toHaveBeenCalled();

      expect(result.lateCalculation.isLate).toBe(true);
      expect(result.lateCalculation.lateFee).toBe(240); // 1 day * $10/hr * 24 = $240
    });

    it('should mark as completed when returned on time', async () => {
      const actualReturn = new Date('2023-06-01T09:30:00Z'); // Returned early

      mockRental.update.mockResolvedValue({
        ...mockRental,
        status: 'completed',
        actualReturnDateTime: actualReturn,
        payoutStatus: 'pending'
      });

      const result = await LateReturnService.processLateReturn('123', actualReturn);

      expect(mockRental.update).toHaveBeenCalledWith({
        actualReturnDateTime: actualReturn,
        status: 'completed',
        payoutStatus: 'pending'
      });

      expect(emailService.customerService.sendLateReturnToCustomerService).not.toHaveBeenCalled();
      expect(result.lateCalculation.isLate).toBe(false);
    });

    it('should throw error when rental not found', async () => {
      Rental.findByPk.mockResolvedValue(null);

      await expect(
        LateReturnService.processLateReturn('nonexistent', new Date())
      ).rejects.toThrow('Rental not found');
    });

    it('should throw error when rental is not active', async () => {
      mockRental.status = 'completed';

      await expect(
        LateReturnService.processLateReturn('123', new Date())
      ).rejects.toThrow('Can only process late returns for active rentals');
    });
  });
});