import {
    compareByCreatedAt,
    compareByCreatedAtAscending,
    compareByDisplayName,
    compareByLastActive,
    compareByLastSeen,
    compareByLocation,
    compareByLocationAt,
    compareByMemberCount,
    compareByName,
    compareByPrivate,
    compareByStatus,
    compareByUpdatedAt
} from '../compare';

describe('Compare Functions', () => {
    describe('compareByName', () => {
        test('compares objects by name property', () => {
            const a = { name: 'Alice' };
            const b = { name: 'Bob' };
            expect(compareByName(a, b)).toBeLessThan(0);
            expect(compareByName(b, a)).toBeGreaterThan(0);
        });

        test('returns 0 for equal names', () => {
            const a = { name: 'Alice' };
            const b = { name: 'Alice' };
            expect(compareByName(a, b)).toBe(0);
        });

        test('handles non-string name properties', () => {
            const a = { name: null };
            const b = { name: 'Bob' };
            expect(compareByName(a, b)).toBe(0);

            const c = { name: 123 };
            const d = { name: 'Alice' };
            expect(compareByName(c, d)).toBe(0);
        });

        test('handles missing name properties', () => {
            const a = {};
            const b = { name: 'Bob' };
            expect(compareByName(a, b)).toBe(0);
        });

        test('uses locale-aware comparison', () => {
            const a = { name: 'ä' };
            const b = { name: 'z' };
            expect(typeof compareByName(a, b)).toBe('number');
        });
    });

    describe('compareByCreatedAt', () => {
        test('compares objects by created_at in descending order', () => {
            const a = { created_at: '2023-01-01' };
            const b = { created_at: '2023-01-02' };
            expect(compareByCreatedAt(a, b)).toBeGreaterThan(0);
            expect(compareByCreatedAt(b, a)).toBeLessThan(0);
        });

        test('returns 0 for equal created_at', () => {
            const a = { created_at: '2023-01-01' };
            const b = { created_at: '2023-01-01' };
            expect(compareByCreatedAt(a, b)).toBe(0);
        });

        test('handles non-string created_at properties', () => {
            const a = { created_at: null };
            const b = { created_at: '2023-01-01' };
            expect(compareByCreatedAt(a, b)).toBe(0);

            const c = { created_at: 123 };
            const d = { created_at: '2023-01-01' };
            expect(compareByCreatedAt(c, d)).toBe(0);
        });

        test('handles case-insensitive comparison', () => {
            const a = { created_at: '2023-01-01t12:00:00z' };
            const b = { created_at: '2023-01-01T12:00:00Z' };
            expect(compareByCreatedAt(a, b)).toBe(0);
        });
    });

    describe('compareByCreatedAtAscending', () => {
        test('compares objects by created_at in ascending order', () => {
            const a = { created_at: '2023-01-01' };
            const b = { created_at: '2023-01-02' };
            expect(compareByCreatedAtAscending(a, b)).toBeLessThan(0);
            expect(compareByCreatedAtAscending(b, a)).toBeGreaterThan(0);
        });

        test('returns 0 for equal created_at', () => {
            const a = { created_at: '2023-01-01' };
            const b = { created_at: '2023-01-01' };
            expect(compareByCreatedAtAscending(a, b)).toBe(0);
        });

        test('handles undefined created_at', () => {
            const a = { created_at: undefined };
            const b = { created_at: '2023-01-01' };
            // undefined comparison with string: both undefined < string and undefined > string are false
            // So it falls through to return 0
            expect(compareByCreatedAtAscending(a, b)).toBe(0);
        });
    });

    describe('compareByUpdatedAt', () => {
        test('compares objects by updated_at in descending order', () => {
            const a = { updated_at: '2023-01-01' };
            const b = { updated_at: '2023-01-02' };
            expect(compareByUpdatedAt(a, b)).toBeGreaterThan(0);
            expect(compareByUpdatedAt(b, a)).toBeLessThan(0);
        });

        test('returns 0 for equal updated_at', () => {
            const a = { updated_at: '2023-01-01' };
            const b = { updated_at: '2023-01-01' };
            expect(compareByUpdatedAt(a, b)).toBe(0);
        });

        test('handles non-string updated_at properties', () => {
            const a = { updated_at: null };
            const b = { updated_at: '2023-01-01' };
            expect(compareByUpdatedAt(a, b)).toBe(0);
        });

        test('handles case-insensitive comparison', () => {
            const a = { updated_at: '2023-01-01t12:00:00z' };
            const b = { updated_at: '2023-01-01T12:00:00Z' };
            expect(compareByUpdatedAt(a, b)).toBe(0);
        });
    });

    describe('compareByDisplayName', () => {
        test('compares objects by displayName property', () => {
            const a = { displayName: 'Alice Display' };
            const b = { displayName: 'Bob Display' };
            expect(compareByDisplayName(a, b)).toBeLessThan(0);
            expect(compareByDisplayName(b, a)).toBeGreaterThan(0);
        });

        test('returns 0 for equal displayNames', () => {
            const a = { displayName: 'Alice Display' };
            const b = { displayName: 'Alice Display' };
            expect(compareByDisplayName(a, b)).toBe(0);
        });

        test('handles non-string displayName properties', () => {
            const a = { displayName: null };
            const b = { displayName: 'Bob Display' };
            expect(compareByDisplayName(a, b)).toBe(0);

            const c = { displayName: 123 };
            const d = { displayName: 'Alice Display' };
            expect(compareByDisplayName(c, d)).toBe(0);
        });
    });

    describe('compareByMemberCount', () => {
        test('compares objects by memberCount property', () => {
            const a = { memberCount: 5 };
            const b = { memberCount: 10 };
            expect(compareByMemberCount(a, b)).toBe(-5);
            expect(compareByMemberCount(b, a)).toBe(5);
        });

        test('returns 0 for equal memberCounts', () => {
            const a = { memberCount: 5 };
            const b = { memberCount: 5 };
            expect(compareByMemberCount(a, b)).toBe(0);
        });

        test('handles non-number memberCount properties', () => {
            const a = { memberCount: 'invalid' };
            const b = { memberCount: 10 };
            expect(compareByMemberCount(a, b)).toBe(0);

            const c = { memberCount: null };
            const d = { memberCount: 5 };
            expect(compareByMemberCount(c, d)).toBe(0);
        });

        test('handles negative member counts', () => {
            const a = { memberCount: -5 };
            const b = { memberCount: 10 };
            expect(compareByMemberCount(a, b)).toBe(-15);
        });
    });

    describe('compareByPrivate', () => {
        test('prioritizes non-private locations', () => {
            const a = { ref: { location: 'public' } };
            const b = { ref: { location: 'private' } };
            expect(compareByPrivate(a, b)).toBe(-1);
            expect(compareByPrivate(b, a)).toBe(1);
        });

        test('returns 0 when both are private', () => {
            const a = { ref: { location: 'private' } };
            const b = { ref: { location: 'private' } };
            expect(compareByPrivate(a, b)).toBe(0);
        });

        test('returns 0 when both are non-private', () => {
            const a = { ref: { location: 'public' } };
            const b = { ref: { location: 'friends' } };
            expect(compareByPrivate(a, b)).toBe(0);
        });

        test('handles undefined ref properties', () => {
            const a = { ref: undefined };
            const b = { ref: { location: 'private' } };
            expect(compareByPrivate(a, b)).toBe(0);

            const c = {};
            const d = { ref: { location: 'public' } };
            expect(compareByPrivate(c, d)).toBe(0);
        });
    });

    describe('compareByStatus', () => {
        test('handles offline users', () => {
            const a = { ref: { state: 'offline', status: 'active' } };
            const b = { ref: { state: 'online', status: 'busy' } };
            expect(compareByStatus(a, b)).toBe(1);
        });

        test('returns 0 for same status', () => {
            const a = { ref: { status: 'active' } };
            const b = { ref: { status: 'active' } };
            expect(compareByStatus(a, b)).toBe(0);
        });

        test('handles undefined ref properties', () => {
            const a = { ref: undefined };
            const b = { ref: { status: 'active' } };
            expect(compareByStatus(a, b)).toBe(0);
        });
    });

    describe('compareByLastActive', () => {
        test('compares online users by $online_for', () => {
            const a = {
                state: 'online',
                ref: { $online_for: 100 }
            };
            const b = {
                state: 'online',
                ref: { $online_for: 200 }
            };
            expect(compareByLastActive(a, b)).toBe(1);
        });

        test('falls back to last_activity for non-online users', () => {
            const a = {
                state: 'offline',
                ref: { last_activity: '2023-01-01' }
            };
            const b = {
                state: 'offline',
                ref: { last_activity: '2023-01-02' }
            };
            expect(compareByLastActive(a, b)).toBe(1);
        });

        test('handles undefined ref properties', () => {
            const a = { state: 'online', ref: undefined };
            const b = { state: 'online', ref: { $online_for: 100 } };
            expect(compareByLastActive(a, b)).toBe(0);
        });
    });

    describe('compareByLastSeen', () => {
        test('compares by $lastSeen field', () => {
            const a = { ref: { $lastSeen: '2023-01-01' } };
            const b = { ref: { $lastSeen: '2023-01-02' } };
            expect(compareByLastSeen(a, b)).toBe(1);
        });

        test('handles empty string as longest active', () => {
            const a = { ref: { $lastSeen: '' } };
            const b = { ref: { $lastSeen: '2023-01-01' } };
            // '' < '2023-01-01' is true, so first condition matches and returns 1
            expect(compareByLastSeen(a, b)).toBe(1);
        });

        test('handles undefined ref properties', () => {
            const a = { ref: undefined };
            const b = { ref: { $lastSeen: '2023-01-01' } };
            expect(compareByLastSeen(a, b)).toBe(0);
        });
    });

    describe('compareByLocationAt', () => {
        test('handles traveling status', () => {
            const a = { location: 'traveling', $location_at: 100 };
            const b = { location: 'public', $location_at: 200 };
            expect(compareByLocationAt(a, b)).toBe(1);
            expect(compareByLocationAt(b, a)).toBe(-1);
        });

        test('returns 0 when both are traveling', () => {
            const a = { location: 'traveling', $location_at: 100 };
            const b = { location: 'traveling', $location_at: 200 };
            expect(compareByLocationAt(a, b)).toBe(0);
        });

        test('compares by $location_at for non-traveling locations', () => {
            const a = { location: 'public', $location_at: 100 };
            const b = { location: 'friends', $location_at: 200 };
            expect(compareByLocationAt(a, b)).toBe(-1);
            expect(compareByLocationAt(b, a)).toBe(1);
        });

        test('returns 0 for equal $location_at', () => {
            const a = { location: 'public', $location_at: 100 };
            const b = { location: 'friends', $location_at: 100 };
            expect(compareByLocationAt(a, b)).toBe(0);
        });
    });

    describe('compareByLocation', () => {
        test('compares online users by location', () => {
            const a = {
                state: 'online',
                ref: { location: 'public' }
            };
            const b = {
                state: 'online',
                ref: { location: 'friends' }
            };
            expect(compareByLocation(a, b)).toBeGreaterThan(0);
        });

        test('returns 0 for non-online users', () => {
            const a = {
                state: 'offline',
                ref: { location: 'public' }
            };
            const b = {
                state: 'offline',
                ref: { location: 'friends' }
            };
            expect(compareByLocation(a, b)).toBe(0);
        });

        test('returns 0 when one user is not online', () => {
            const a = {
                state: 'online',
                ref: { location: 'public' }
            };
            const b = {
                state: 'offline',
                ref: { location: 'friends' }
            };
            expect(compareByLocation(a, b)).toBe(0);
        });

        test('handles undefined ref properties', () => {
            const a = { state: 'online', ref: undefined };
            const b = { state: 'online', ref: { location: 'public' } };
            expect(compareByLocation(a, b)).toBe(0);
        });

        test('uses locale-aware comparison', () => {
            const a = {
                state: 'online',
                ref: { location: 'ä' }
            };
            const b = {
                state: 'online',
                ref: { location: 'z' }
            };
            expect(typeof compareByLocation(a, b)).toBe('number');
        });
    });

    describe('edge cases and boundary conditions', () => {
        test('handles null objects', () => {
            // compareByName doesn't handle null objects - it will throw
            expect(() => compareByName(null, { name: 'test' })).toThrow();
            expect(() => compareByName({ name: 'test' }, null)).toThrow();
        });

        test('handles empty objects', () => {
            expect(compareByName({}, {})).toBe(0);
            expect(compareByDisplayName({}, {})).toBe(0);
            expect(compareByMemberCount({}, {})).toBe(0);
        });

        test('handles mixed valid and invalid data', () => {
            const valid = { memberCount: 5 };
            const invalid = { memberCount: 'not a number' };
            expect(compareByMemberCount(valid, invalid)).toBe(0);
        });

        test('handles zero values', () => {
            const a = { memberCount: 0 };
            const b = { memberCount: 5 };
            expect(compareByMemberCount(a, b)).toBe(-5);
        });
    });
});
