const S3OwnershipService = require('../../../services/s3OwnershipService');
const { Message, ConditionCheck, Rental } = require('../../../models');

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

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

  describe('getFileTypeFromKey', () => {
    it('should return "profile" for profiles folder', () => {
      expect(S3OwnershipService.getFileTypeFromKey('profiles/uuid.jpg')).toBe('profile');
    });

    it('should return "item" for items folder', () => {
      expect(S3OwnershipService.getFileTypeFromKey('items/uuid.jpg')).toBe('item');
    });

    it('should return "message" for messages folder', () => {
      expect(S3OwnershipService.getFileTypeFromKey('messages/uuid.jpg')).toBe('message');
    });

    it('should return "forum" for forum folder', () => {
      expect(S3OwnershipService.getFileTypeFromKey('forum/uuid.jpg')).toBe('forum');
    });

    it('should return "condition-check" for condition-checks folder', () => {
      expect(S3OwnershipService.getFileTypeFromKey('condition-checks/uuid.jpg')).toBe('condition-check');
    });

    it('should return null for unknown folder', () => {
      expect(S3OwnershipService.getFileTypeFromKey('unknown/uuid.jpg')).toBeNull();
    });

    it('should return null for null key', () => {
      expect(S3OwnershipService.getFileTypeFromKey(null)).toBeNull();
    });

    it('should return null for undefined key', () => {
      expect(S3OwnershipService.getFileTypeFromKey(undefined)).toBeNull();
    });

    it('should return null for empty string', () => {
      expect(S3OwnershipService.getFileTypeFromKey('')).toBeNull();
    });
  });

  describe('canAccessFile', () => {
    describe('public folders', () => {
      it('should authorize access to profile images for any user', async () => {
        const result = await S3OwnershipService.canAccessFile('profiles/uuid.jpg', 'user-123');

        expect(result).toEqual({ authorized: true });
      });

      it('should authorize access to item images for any user', async () => {
        const result = await S3OwnershipService.canAccessFile('items/uuid.jpg', 'user-123');

        expect(result).toEqual({ authorized: true });
      });

      it('should authorize access to forum images for any user', async () => {
        const result = await S3OwnershipService.canAccessFile('forum/uuid.jpg', 'user-123');

        expect(result).toEqual({ authorized: true });
      });
    });

    describe('private folders', () => {
      it('should call verifyMessageAccess for message images', async () => {
        Message.findOne.mockResolvedValue({ id: 'msg-123' });

        const result = await S3OwnershipService.canAccessFile('messages/uuid.jpg', 'user-123');

        expect(Message.findOne).toHaveBeenCalled();
        expect(result.authorized).toBe(true);
      });

      it('should call verifyConditionCheckAccess for condition-check images', async () => {
        ConditionCheck.findOne.mockResolvedValue({ id: 'check-123' });

        const result = await S3OwnershipService.canAccessFile('condition-checks/uuid.jpg', 'user-123');

        expect(ConditionCheck.findOne).toHaveBeenCalled();
        expect(result.authorized).toBe(true);
      });
    });

    describe('unknown file types', () => {
      it('should deny access for unknown folder', async () => {
        const result = await S3OwnershipService.canAccessFile('unknown/uuid.jpg', 'user-123');

        expect(result).toEqual({
          authorized: false,
          reason: 'Unknown file type'
        });
      });
    });
  });

  describe('verifyMessageAccess', () => {
    const testKey = 'messages/550e8400-e29b-41d4-a716-446655440000.jpg';
    const senderId = 'sender-123';
    const receiverId = 'receiver-456';

    it('should authorize sender to access message image', async () => {
      Message.findOne.mockResolvedValue({
        id: 'msg-123',
        senderId,
        receiverId,
        imageFilename: testKey
      });

      const result = await S3OwnershipService.verifyMessageAccess(testKey, senderId);

      expect(result).toEqual({
        authorized: true,
        reason: null
      });

      expect(Message.findOne).toHaveBeenCalledWith({
        where: expect.objectContaining({
          imageFilename: testKey
        })
      });
    });

    it('should authorize receiver to access message image', async () => {
      Message.findOne.mockResolvedValue({
        id: 'msg-123',
        senderId,
        receiverId,
        imageFilename: testKey
      });

      const result = await S3OwnershipService.verifyMessageAccess(testKey, receiverId);

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

    it('should deny access to unauthorized user', async () => {
      Message.findOne.mockResolvedValue(null);

      const result = await S3OwnershipService.verifyMessageAccess(testKey, 'other-user');

      expect(result).toEqual({
        authorized: false,
        reason: 'Not a participant in this message'
      });
    });

    it('should deny access when message does not exist', async () => {
      Message.findOne.mockResolvedValue(null);

      const result = await S3OwnershipService.verifyMessageAccess('messages/nonexistent.jpg', senderId);

      expect(result.authorized).toBe(false);
      expect(result.reason).toBe('Not a participant in this message');
    });
  });

  describe('verifyConditionCheckAccess', () => {
    const testKey = 'condition-checks/550e8400-e29b-41d4-a716-446655440000.jpg';
    const ownerId = 'owner-123';
    const renterId = 'renter-456';

    it('should authorize owner to access condition check image', async () => {
      ConditionCheck.findOne.mockResolvedValue({
        id: 'check-123',
        imageFilenames: [testKey],
        rental: {
          id: 'rental-123',
          ownerId,
          renterId
        }
      });

      const result = await S3OwnershipService.verifyConditionCheckAccess(testKey, ownerId);

      expect(result).toEqual({
        authorized: true,
        reason: null
      });

      expect(ConditionCheck.findOne).toHaveBeenCalledWith({
        where: expect.objectContaining({
          imageFilenames: expect.anything()
        }),
        include: expect.arrayContaining([
          expect.objectContaining({
            model: Rental,
            as: 'rental'
          })
        ])
      });
    });

    it('should authorize renter to access condition check image', async () => {
      ConditionCheck.findOne.mockResolvedValue({
        id: 'check-123',
        imageFilenames: [testKey],
        rental: {
          id: 'rental-123',
          ownerId,
          renterId
        }
      });

      const result = await S3OwnershipService.verifyConditionCheckAccess(testKey, renterId);

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

    it('should deny access to unauthorized user', async () => {
      ConditionCheck.findOne.mockResolvedValue(null);

      const result = await S3OwnershipService.verifyConditionCheckAccess(testKey, 'other-user');

      expect(result).toEqual({
        authorized: false,
        reason: 'Not a participant in this rental'
      });
    });

    it('should deny access when condition check does not exist', async () => {
      ConditionCheck.findOne.mockResolvedValue(null);

      const result = await S3OwnershipService.verifyConditionCheckAccess(
        'condition-checks/nonexistent.jpg',
        ownerId
      );

      expect(result.authorized).toBe(false);
      expect(result.reason).toBe('Not a participant in this rental');
    });

    it('should use Op.contains for imageFilenames array search', async () => {
      const { Op } = require('sequelize');

      ConditionCheck.findOne.mockResolvedValue(null);

      await S3OwnershipService.verifyConditionCheckAccess(testKey, ownerId);

      expect(ConditionCheck.findOne).toHaveBeenCalledWith(
        expect.objectContaining({
          where: {
            imageFilenames: expect.anything()
          }
        })
      );
    });
  });
});
