import { IdentityDomainInvariantError, IdentityDomainValidationError } from '../errors/IdentityDomainError';
import { RatingDelta } from '../value-objects/RatingDelta';
import { RatingDimensionKey } from '../value-objects/RatingDimensionKey';
import { RatingEventId } from '../value-objects/RatingEventId';
import { RatingEvent } from './RatingEvent';

describe('RatingEvent', () => {
  const validProps = {
    id: RatingEventId.create('123e4567-e89b-12d3-a456-426614174000'),
    userId: 'user-123',
    dimension: RatingDimensionKey.create('driving'),
    delta: RatingDelta.create(10),
    occurredAt: new Date('2024-01-01T00:00:00Z'),
    createdAt: new Date('2024-01-01T00:00:00Z'),
    source: {
      type: 'race' as const,
      id: 'race-456',
    },
    reason: {
      code: 'DRIVING_FINISH_STRENGTH_GAIN',
      summary: 'Finished 3rd in strong field',
      details: { position: 3, fieldStrength: 2500 },
    },
    visibility: {
      public: true,
      redactedFields: [] as string[],
    },
    version: 1,
  };

  describe('create', () => {
    it('should create a valid rating event', () => {
      const event = RatingEvent.create(validProps);
      
      expect(event.id.value).toBe(validProps.id.value);
      expect(event.userId).toBe(validProps.userId);
      expect(event.dimension.value).toBe('driving');
      expect(event.delta.value).toBe(10);
      expect(event.occurredAt).toEqual(validProps.occurredAt);
      expect(event.source.type).toBe('race');
      expect(event.reason.code).toBe('DRIVING_FINISH_STRENGTH_GAIN');
      expect(event.visibility.public).toBe(true);
      expect(event.version).toBe(1);
    });

    it('should create event with optional weight', () => {
      const props = { ...validProps, weight: 2 };
      const event = RatingEvent.create(props);
      
      expect(event.weight).toBe(2);
    });

    it('should create event with non-public visibility', () => {
      const props = {
        ...validProps,
        visibility: { public: false, redactedFields: ['reason.summary'] },
      };
      const event = RatingEvent.create(props);
      
      expect(event.visibility.public).toBe(false);
      expect(event.visibility.redactedFields).toEqual(['reason.summary']);
    });

    it('should throw for missing userId', () => {
      const props = { ...validProps, userId: '' };
      expect(() => RatingEvent.create(props)).toThrow(IdentityDomainValidationError);
    });

    it('should throw for missing dimension', () => {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { dimension: _dimension, ...rest } = validProps;
      expect(() => RatingEvent.create(rest as typeof validProps)).toThrow(IdentityDomainValidationError);
    });

    it('should throw for missing delta', () => {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { delta: _delta, ...rest } = validProps;
      expect(() => RatingEvent.create(rest as typeof validProps)).toThrow(IdentityDomainValidationError);
    });

    it('should throw for missing source', () => {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { source: _source, ...rest } = validProps;
      expect(() => RatingEvent.create(rest as typeof validProps)).toThrow(IdentityDomainValidationError);
    });

    it('should throw for missing reason', () => {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { reason: _reason, ...rest } = validProps;
      expect(() => RatingEvent.create(rest as typeof validProps)).toThrow(IdentityDomainValidationError);
    });

    it('should throw for missing visibility', () => {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { visibility: _visibility, ...rest } = validProps;
      expect(() => RatingEvent.create(rest as typeof validProps)).toThrow(IdentityDomainValidationError);
    });

    it('should throw for invalid version', () => {
      const props = { ...validProps, version: 0 };
      expect(() => RatingEvent.create(props)).toThrow(IdentityDomainValidationError);
    });

    it('should throw for future occurredAt', () => {
      const futureDate = new Date(Date.now() + 86400000);
      const props = { ...validProps, occurredAt: futureDate };
      expect(() => RatingEvent.create(props)).toThrow(IdentityDomainValidationError);
    });

    it('should throw for future createdAt', () => {
      const futureDate = new Date(Date.now() + 86400000);
      const props = { ...validProps, createdAt: futureDate };
      expect(() => RatingEvent.create(props)).toThrow(IdentityDomainValidationError);
    });

    it('should throw for occurredAt after createdAt', () => {
      const props = {
        ...validProps,
        occurredAt: new Date('2024-01-02T00:00:00Z'),
        createdAt: new Date('2024-01-01T00:00:00Z'),
      };
      expect(() => RatingEvent.create(props)).toThrow(IdentityDomainInvariantError);
    });
  });

  describe('rehydrate', () => {
    it('should rehydrate event from stored data', () => {
      const event = RatingEvent.rehydrate(validProps);
      
      expect(event.id.value).toBe(validProps.id.value);
      expect(event.userId).toBe(validProps.userId);
      expect(event.dimension.value).toBe('driving');
    });

    it('should rehydrate with optional weight', () => {
      const props = { ...validProps, weight: 2 };
      const event = RatingEvent.rehydrate(props);
      
      expect(event.weight).toBe(2);
    });
  });

  describe('equals', () => {
    it('should return true for same ID', () => {
      const event1 = RatingEvent.create(validProps);
      const event2 = RatingEvent.rehydrate(validProps);
      
      expect(event1.equals(event2)).toBe(true);
    });

    it('should return false for different IDs', () => {
      const event1 = RatingEvent.create(validProps);
      const event2 = RatingEvent.create({
        ...validProps,
        id: RatingEventId.create('123e4567-e89b-12d3-a456-426614174001'),
      });
      
      expect(event1.equals(event2)).toBe(false);
    });
  });

  describe('toJSON', () => {
    it('should return plain object representation', () => {
      const event = RatingEvent.create(validProps);
      const json = event.toJSON();
      
      expect(json.id).toBe(validProps.id.value);
      expect(json.userId).toBe(validProps.userId);
      expect(json.dimension).toBe('driving');
      expect(json.delta).toBe(10);
      expect(json.source).toEqual({ type: 'race', id: 'race-456' });
    });
  });
});