import { IdentityDomainValidationError } from '../errors/IdentityDomainError';
import { ExternalRatingProvenance } from './ExternalRatingProvenance';

describe('ExternalRatingProvenance', () => {
  describe('create', () => {
    it('should create a valid provenance with default verified=false', () => {
      const now = new Date();
      const provenance = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: now,
      });

      expect(provenance.source).toBe('iracing');
      expect(provenance.lastSyncedAt).toBe(now);
      expect(provenance.verified).toBe(false);
    });

    it('should create a valid provenance with verified=true', () => {
      const now = new Date();
      const provenance = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: now,
        verified: true,
      });

      expect(provenance.verified).toBe(true);
    });

    it('should trim source string', () => {
      const now = new Date();
      const provenance = ExternalRatingProvenance.create({
        source: '  iracing  ',
        lastSyncedAt: now,
      });

      expect(provenance.source).toBe('iracing');
    });

    it('should throw error for empty source', () => {
      const now = new Date();
      expect(() =>
        ExternalRatingProvenance.create({
          source: '',
          lastSyncedAt: now,
        })
      ).toThrow(IdentityDomainValidationError);
      expect(() =>
        ExternalRatingProvenance.create({
          source: '   ',
          lastSyncedAt: now,
        })
      ).toThrow(IdentityDomainValidationError);
    });

    it('should throw error for invalid date', () => {
      expect(() =>
        ExternalRatingProvenance.create({
          source: 'iracing',
          lastSyncedAt: new Date('invalid'),
        })
      ).toThrow(IdentityDomainValidationError);
    });
  });

  describe('restore', () => {
    it('should restore provenance from stored props', () => {
      const now = new Date();
      const provenance = ExternalRatingProvenance.restore({
        source: 'iracing',
        lastSyncedAt: now,
        verified: true,
      });

      expect(provenance.source).toBe('iracing');
      expect(provenance.lastSyncedAt).toBe(now);
      expect(provenance.verified).toBe(true);
    });

    it('should default verified to false when not provided', () => {
      const now = new Date();
      const provenance = ExternalRatingProvenance.restore({
        source: 'iracing',
        lastSyncedAt: now,
      });

      expect(provenance.verified).toBe(false);
    });
  });

  describe('equals', () => {
    it('should return true for identical provenance', () => {
      const now = new Date();
      const p1 = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: now,
        verified: true,
      });
      const p2 = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: now,
        verified: true,
      });

      expect(p1.equals(p2)).toBe(true);
    });

    it('should return false for different source', () => {
      const now = new Date();
      const p1 = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: now,
      });
      const p2 = ExternalRatingProvenance.create({
        source: 'simracing',
        lastSyncedAt: now,
      });

      expect(p1.equals(p2)).toBe(false);
    });

    it('should return false for different lastSyncedAt', () => {
      const p1 = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: new Date('2024-01-01'),
      });
      const p2 = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: new Date('2024-01-02'),
      });

      expect(p1.equals(p2)).toBe(false);
    });

    it('should return false for different verified', () => {
      const now = new Date();
      const p1 = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: now,
        verified: true,
      });
      const p2 = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: now,
        verified: false,
      });

      expect(p1.equals(p2)).toBe(false);
    });
  });

  describe('toString', () => {
    it('should return string representation', () => {
      const now = new Date('2024-01-01T00:00:00Z');
      const provenance = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: now,
        verified: true,
      });

      expect(provenance.toString()).toBe('iracing:2024-01-01T00:00:00.000Z:verified');
    });
  });

  describe('markVerified', () => {
    it('should return new provenance with verified=true', () => {
      const now = new Date();
      const original = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: now,
        verified: false,
      });

      const verified = original.markVerified();

      expect(verified.verified).toBe(true);
      expect(verified.source).toBe(original.source);
      expect(verified.lastSyncedAt).toBe(original.lastSyncedAt);
      expect(original.verified).toBe(false); // Original unchanged
    });
  });

  describe('updateLastSyncedAt', () => {
    it('should return new provenance with updated date', () => {
      const now = new Date('2024-01-01');
      const newDate = new Date('2024-01-02');
      const original = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: now,
        verified: true,
      });

      const updated = original.updateLastSyncedAt(newDate);

      expect(updated.lastSyncedAt).toBe(newDate);
      expect(updated.source).toBe(original.source);
      expect(updated.verified).toBe(original.verified);
      expect(original.lastSyncedAt).toBe(now); // Original unchanged
    });
  });

  describe('props', () => {
    it('should return correct props object', () => {
      const now = new Date();
      const provenance = ExternalRatingProvenance.create({
        source: 'iracing',
        lastSyncedAt: now,
        verified: true,
      });

      const props = provenance.props;

      expect(props.source).toBe('iracing');
      expect(props.lastSyncedAt).toBe(now);
      expect(props.verified).toBe(true);
    });
  });
});