import {
    getTimezones,
    getTimezoneOptions,
    groupTimezonesByContinent,
    normalizeTimezone,
} from '../utils';
import type { TimezoneOption } from '../types';

describe('TimezoneSelector utils', () => {
    describe('getTimezones', () => {
        test('returns an array of SuiteTimezone objects', () => {
            const timezones = getTimezones();

            expect(timezones.length).toBeGreaterThan(300);
            expect(timezones.at(0)).toEqual({
                continent: 'Oceania',
                id: 'pacific/midway',
                offset: -11,
                utc: 'UTC-11:00',
            });
        });

        test('correctly maps Suite continents', () => {
            const timezones = getTimezones();
            const southAmericanCity = timezones.find((tz) => tz.id.includes('argentina'));
            const northAmericanCity = timezones.find((tz) => tz.id.includes('new_york'));
            const europeanCity = timezones.find((tz) => tz.id.includes('london'));

            expect(southAmericanCity?.continent).toBe('South America');
            expect(northAmericanCity?.continent).toBe('North America');
            expect(europeanCity?.continent).toBe('Europe');
        });

        test('should format UTC offset correctly', () => {
            const timezones = getTimezones();
            const utcTimezone = timezones.find((tz) => tz.offset === 0);
            const positiveOffset = timezones.find((tz) => tz.offset === 1);
            const negativeOffset = timezones.find((tz) => tz.offset === -2);

            expect(utcTimezone?.utc).toBe('UTC±00:00');
            expect(positiveOffset?.utc).toBe('UTC+01:00');
            expect(negativeOffset?.utc).toBe('UTC-02:00');
        });

        test('should sort timezones by offset', () => {
            const timezones = getTimezones();

            // check first item has the lower offset and last item has the higher offset
            expect(timezones.at(0)?.offset).toBe(-11);
            expect(timezones.at(-1)?.offset).toBe(14);
        });

        test('calculates offsets for a specific date', () => {
            // Test with a summer date (July) and winter date (January)
            const summerDate = new Date('2024-07-15');
            const winterDate = new Date('2024-01-15');

            const summerTimezones = getTimezones(summerDate);
            const winterTimezones = getTimezones(winterDate);

            // Find New York timezone in both
            const summerNY = summerTimezones.find((tz) => tz.id === 'america/new_york');
            const winterNY = winterTimezones.find((tz) => tz.id === 'america/new_york');

            // New York should be UTC-4 in summer (DST) and UTC-5 in winter
            expect(summerNY?.offset).toBe(-4);
            expect(summerNY?.utc).toBe('UTC-04:00');
            expect(winterNY?.offset).toBe(-5);
            expect(winterNY?.utc).toBe('UTC-05:00');
        });
    });

    describe('getTimezoneOptions', () => {
        test('displays correct timezone names for date (Standard vs Daylight Time)', () => {
            const summerDate = new Date('2024-07-15');
            const winterDate = new Date('2024-01-15');

            const summerOptions = getTimezoneOptions(false, summerDate);
            const winterOptions = getTimezoneOptions(false, winterDate);

            const summerNY = summerOptions.find((tz) => tz.value === 'america/new_york');
            const winterNY = winterOptions.find((tz) => tz.value === 'america/new_york');

            expect(summerNY?.label).toContain('Eastern Daylight Time');
            expect(winterNY?.label).toContain('Eastern Standard Time');
        });

        test('returns timezones in Select ListViewOptions format', () => {
            const options = getTimezoneOptions();

            expect(options.length).toBeGreaterThan(300);

            // Test the structure and properties of the first option
            // we use this because different node versions may return different order
            // due to changes in Intl.DateTimeFormat behavior affecting timezone name sorting
            const firstOption = options.at(0);
            expect(firstOption).toBeDefined();
            expect(firstOption).toHaveProperty('continent');
            expect(firstOption).toHaveProperty('id');
            expect(firstOption).toHaveProperty('label');
            expect(firstOption).toHaveProperty('offset');
            expect(firstOption).toHaveProperty('utc');
            expect(firstOption).toHaveProperty('value');

            // Test that the first option has the lowest offset (should be -11)
            expect(firstOption!.offset).toBe(-11);
            expect(firstOption!.utc).toBe('UTC-11:00');
            expect(firstOption!.continent).toBe('Oceania');

            // Test that all options are properly sorted by offset, then by label
            const sortingViolations = [];
            for (let i = 1; i < options.length; i++) {
                const current = options[i];
                const previous = options[i - 1];

                if (current.offset === previous.offset) {
                    // Same offset: should be sorted alphabetically by label
                    if (current.label.localeCompare(previous.label) < 0) {
                        sortingViolations.push(
                            `Labels not sorted: "${previous.label}" should come after "${current.label}"`
                        );
                    }
                } else if (current.offset < previous.offset) {
                    // Different offset: should be sorted by offset ascending
                    sortingViolations.push(
                        `Offsets not sorted: ${previous.offset} should come after ${current.offset}`
                    );
                }
            }
            expect(sortingViolations).toHaveLength(0);
        });
    });

    describe('groupTimezonesByContinent', () => {
        test('groups timezone options by continent', () => {
            const options: TimezoneOption[] = [
                {
                    offset: 0,
                    continent: 'Europe',
                    value: 'Europe/London',
                    utc: 'UTC±00,00',
                    label: '(UTC±00:00) British Time - London',
                },
                {
                    offset: -4,
                    continent: 'North America',
                    value: 'America/New_York',
                    utc: 'UTC-04,00',
                    label: '(UTC-04:00) Eastern Time - New York',
                },
            ];

            const grouped = groupTimezonesByContinent(options);
            expect(grouped.length).toBe(2);
            expect(grouped[0].key).toBe('Europe');
            expect(grouped[1].key).toBe('North America');
            expect(grouped[0].options.length).toBe(1);
            expect(grouped[1].options.length).toBe(1);
        });

        test('handles timezones with unknown continent', () => {
            const options: TimezoneOption[] = [
                {
                    offset: 0,
                    value: 'Unknown/Zone',
                    label: '(UTC±00:00) Unknown Time',
                    continent: 'Unknown',
                    utc: 'UTC±00,00',
                },
            ];

            const grouped = groupTimezonesByContinent(options);
            expect(grouped.length).toBe(1);
            expect(grouped[0].key).toBe('Unknown');
        });
    });

    describe('normalizeTimezone', () => {
        test('converts string timezone to lowercase', () => {
            expect(normalizeTimezone('America/New_York')).toBe('america/new_york');
            expect(normalizeTimezone('EUROPE/LONDON')).toBe('europe/london');
        });

        test('handles timezone option object', () => {
            const tzOption: Partial<TimezoneOption> = {
                value: 'America/New_York',
                label: '(UTC-04:00) Eastern Time - New York',
            };
            expect(normalizeTimezone(tzOption)).toEqual({
                label: '(UTC-04:00) Eastern Time - New York',
                value: 'america/new_york',
            });
        });
    });
});
