const ConditionCheckService = require('../../../services/conditionCheckService');
const { ConditionCheck, Rental, User } = require('../../../models');

jest.mock('../../../models');

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

  describe('submitConditionCheck', () => {
    // Set rental dates relative to current time for valid time window
    // Active status is computed: confirmed + startDateTime in the past
    const now = new Date();
    const mockRental = {
      id: 'rental-123',
      ownerId: 'owner-456',
      renterId: 'renter-789',
      startDateTime: new Date(now.getTime() - 1000 * 60 * 60), // 1 hour ago
      endDateTime: new Date(now.getTime() + 1000 * 60 * 60 * 24), // 24 hours from now
      status: 'confirmed' // Will be computed as "active" since startDateTime is in the past
    };

    const mockPhotos = ['/uploads/photo1.jpg', '/uploads/photo2.jpg'];

    beforeEach(() => {
      Rental.findByPk.mockResolvedValue(mockRental);
      ConditionCheck.findOne.mockResolvedValue(null); // No existing check
      ConditionCheck.create.mockResolvedValue({
        id: 'check-123',
        rentalId: 'rental-123',
        checkType: 'rental_start_renter',
        photos: mockPhotos,
        notes: 'Item received in good condition',
        submittedBy: 'renter-789'
      });
    });

    it('should submit condition check with photos and notes', async () => {
      const result = await ConditionCheckService.submitConditionCheck(
        'rental-123',
        'rental_start_renter',
        'renter-789',
        mockPhotos,
        'Item received in good condition'
      );

      expect(ConditionCheck.create).toHaveBeenCalledWith(
        expect.objectContaining({
          rentalId: 'rental-123',
          checkType: 'rental_start_renter',
          submittedBy: 'renter-789',
          imageFilenames: mockPhotos,
          notes: 'Item received in good condition',
        })
      );

      expect(result).toBeTruthy();
      expect(result.id).toBe('check-123');
    });

    it('should validate user authorization - owner checks', async () => {
      // Renter trying to submit pre-rental owner check
      await expect(
        ConditionCheckService.submitConditionCheck(
          'rental-123',
          'pre_rental_owner',
          'renter-789',
          mockPhotos
        )
      ).rejects.toThrow('Only the item owner can submit owner condition checks');
    });

    it('should validate user authorization - renter checks', async () => {
      // Owner trying to submit rental start renter check
      await expect(
        ConditionCheckService.submitConditionCheck(
          'rental-123',
          'rental_start_renter',
          'owner-456',
          mockPhotos
        )
      ).rejects.toThrow('Only the renter can submit renter condition checks');
    });

    it('should prevent duplicate condition checks', async () => {
      ConditionCheck.findOne.mockResolvedValue({ id: 'existing-check' });

      await expect(
        ConditionCheckService.submitConditionCheck(
          'rental-123',
          'rental_start_renter',
          'renter-789',
          mockPhotos
        )
      ).rejects.toThrow('Condition check already submitted for this type');
    });

    it('should limit number of photos to 20', async () => {
      const tooManyPhotos = Array(21).fill('/uploads/photo.jpg');

      await expect(
        ConditionCheckService.submitConditionCheck(
          'rental-123',
          'rental_start_renter',
          'renter-789',
          tooManyPhotos
        )
      ).rejects.toThrow('Maximum 20 photos allowed per condition check');
    });

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

      await expect(
        ConditionCheckService.submitConditionCheck(
          'nonexistent-rental',
          'rental_start_renter',
          'renter-789',
          mockPhotos
        )
      ).rejects.toThrow('Rental not found');
    });

    it('should allow empty photos array', async () => {
      ConditionCheck.create.mockResolvedValue({
        id: 'check-123',
        rentalId: 'rental-123',
        checkType: 'rental_start_renter',
        photos: [],
        notes: 'No photos',
        submittedBy: 'renter-789'
      });

      const result = await ConditionCheckService.submitConditionCheck(
        'rental-123',
        'rental_start_renter',
        'renter-789',
        [],
        'No photos'
      );

      expect(result).toBeTruthy();
    });

    it('should allow null notes', async () => {
      ConditionCheck.create.mockResolvedValue({
        id: 'check-123',
        rentalId: 'rental-123',
        checkType: 'rental_start_renter',
        photos: mockPhotos,
        notes: null,
        submittedBy: 'renter-789'
      });

      const result = await ConditionCheckService.submitConditionCheck(
        'rental-123',
        'rental_start_renter',
        'renter-789',
        mockPhotos
      );

      expect(result).toBeTruthy();
    });
  });

  describe('validateConditionCheck', () => {
    const now = new Date();
    const mockRental = {
      id: 'rental-123',
      ownerId: 'owner-456',
      renterId: 'renter-789',
      startDateTime: new Date(now.getTime() - 1000 * 60 * 60),
      endDateTime: new Date(now.getTime() + 1000 * 60 * 60 * 24),
      status: 'confirmed'
    };

    beforeEach(() => {
      Rental.findByPk.mockResolvedValue(mockRental);
      ConditionCheck.findOne.mockResolvedValue(null);
    });

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

      const result = await ConditionCheckService.validateConditionCheck(
        'nonexistent',
        'rental_start_renter',
        'renter-789'
      );

      expect(result.canSubmit).toBe(false);
      expect(result.reason).toBe('Rental not found');
    });

    it('should reject owner check by renter', async () => {
      const result = await ConditionCheckService.validateConditionCheck(
        'rental-123',
        'pre_rental_owner',
        'renter-789'
      );

      expect(result.canSubmit).toBe(false);
      expect(result.reason).toContain('owner');
    });

    it('should reject renter check by owner', async () => {
      const result = await ConditionCheckService.validateConditionCheck(
        'rental-123',
        'rental_start_renter',
        'owner-456'
      );

      expect(result.canSubmit).toBe(false);
      expect(result.reason).toContain('renter');
    });

    it('should reject duplicate checks', async () => {
      ConditionCheck.findOne.mockResolvedValue({ id: 'existing' });

      const result = await ConditionCheckService.validateConditionCheck(
        'rental-123',
        'rental_start_renter',
        'renter-789'
      );

      expect(result.canSubmit).toBe(false);
      expect(result.reason).toContain('already submitted');
    });

    it('should return canSubmit false for invalid check type', async () => {
      const result = await ConditionCheckService.validateConditionCheck(
        'rental-123',
        'invalid_type',
        'owner-456'
      );

      expect(result.canSubmit).toBe(false);
      expect(result.reason).toBe('Invalid check type');
    });

    it('should allow post_rental_owner anytime', async () => {
      const result = await ConditionCheckService.validateConditionCheck(
        'rental-123',
        'post_rental_owner',
        'owner-456'
      );

      expect(result.canSubmit).toBe(true);
    });
  });

  describe('getConditionChecksForRentals', () => {
    it('should return empty array for empty rental IDs', async () => {
      const result = await ConditionCheckService.getConditionChecksForRentals([]);
      expect(result).toEqual([]);
    });

    it('should return empty array for null rental IDs', async () => {
      const result = await ConditionCheckService.getConditionChecksForRentals(null);
      expect(result).toEqual([]);
    });

    it('should return condition checks for rentals', async () => {
      const mockChecks = [
        { id: 'check-1', rentalId: 'rental-1', checkType: 'pre_rental_owner' },
        { id: 'check-2', rentalId: 'rental-1', checkType: 'rental_start_renter' },
        { id: 'check-3', rentalId: 'rental-2', checkType: 'pre_rental_owner' },
      ];

      ConditionCheck.findAll.mockResolvedValue(mockChecks);

      const result = await ConditionCheckService.getConditionChecksForRentals(['rental-1', 'rental-2']);

      expect(result).toHaveLength(3);
      expect(ConditionCheck.findAll).toHaveBeenCalled();
    });
  });

  describe('getAvailableChecks', () => {
    it('should return empty array for empty rental IDs', async () => {
      const result = await ConditionCheckService.getAvailableChecks('user-123', []);
      expect(result).toEqual([]);
    });

    it('should return empty array for null rental IDs', async () => {
      const result = await ConditionCheckService.getAvailableChecks('user-123', null);
      expect(result).toEqual([]);
    });

    it('should return available checks for owner', async () => {
      const now = new Date();
      const mockRentals = [{
        id: 'rental-123',
        ownerId: 'owner-456',
        renterId: 'renter-789',
        itemId: 'item-123',
        startDateTime: new Date(now.getTime() + 12 * 60 * 60 * 1000), // 12 hours from now
        endDateTime: new Date(now.getTime() + 36 * 60 * 60 * 1000),
        status: 'confirmed',
      }];

      Rental.findAll.mockResolvedValue(mockRentals);
      Rental.findByPk.mockResolvedValue(mockRentals[0]);
      ConditionCheck.findOne.mockResolvedValue(null);

      const result = await ConditionCheckService.getAvailableChecks('owner-456', ['rental-123']);

      // Should have pre_rental_owner available
      expect(result.length).toBeGreaterThanOrEqual(0);
    });

    it('should return available checks for renter when rental is active', async () => {
      const now = new Date();
      const mockRentals = [{
        id: 'rental-123',
        ownerId: 'owner-456',
        renterId: 'renter-789',
        itemId: 'item-123',
        startDateTime: new Date(now.getTime() - 60 * 60 * 1000), // 1 hour ago
        endDateTime: new Date(now.getTime() + 24 * 60 * 60 * 1000),
        status: 'confirmed',
      }];

      Rental.findAll.mockResolvedValue(mockRentals);
      Rental.findByPk.mockResolvedValue(mockRentals[0]);
      ConditionCheck.findOne.mockResolvedValue(null);

      const result = await ConditionCheckService.getAvailableChecks('renter-789', ['rental-123']);

      // May have rental_start_renter available
      expect(Array.isArray(result)).toBe(true);
    });
  });
});