import {getCurrentLocale} from '@shared/utils';

jest.mock('date-fns/isToday', () => ({
  isToday: jest.fn(),
}));
jest.mock('date-fns/isYesterday', () => ({
  isYesterday: jest.fn(),
}));
jest.mock('@config/i18n', () => ({
  __esModule: true,
  default: {
    t: jest.fn(),
  },
}));
jest.mock('@shared/utils', () => ({
  capitalize: jest.fn(),
  format: jest.fn(),
  getCurrentLocale: jest.fn(),
  getDifferenceInDays: jest.fn(),
  getFormattedDate: jest.fn(),
  getTimeFromUlid: jest.fn(),
}));

let mockIsToday: jest.Mock;
let mockIsYesterday: jest.Mock;
let mockTranslate: jest.Mock;
let mockCapitalize: jest.Mock;
let mockFormat: jest.Mock;
let mockGetCurrentLocale: jest.Mock;
let mockGetDifferenceInDays: jest.Mock;
let mockGetFormattedDate: jest.Mock;
let mockGetTimeFromUlid: jest.Mock;

describe('date utils', () => {
  type Locale = ReturnType<typeof getCurrentLocale>;
  const locale = {code: 'en'} as Locale;
  const date = new Date('2024-01-20T10:30:00.000Z');
  let getHumanizedDateWithTime: typeof import('../date').getHumanizedDateWithTime;
  let getLocalizedDateLabelByTs: typeof import('../date').getLocalizedDateLabelByTs;

  beforeEach(() => {
    jest.resetModules();
    mockIsToday = jest.requireMock('date-fns/isToday').isToday as jest.Mock;
    mockIsYesterday = jest.requireMock('date-fns/isYesterday')
      .isYesterday as jest.Mock;
    mockTranslate = jest.requireMock('@config/i18n').default.t as jest.Mock;
    mockCapitalize = jest.requireMock('@shared/utils').capitalize as jest.Mock;
    mockFormat = jest.requireMock('@shared/utils').format as jest.Mock;
    mockGetCurrentLocale = jest.requireMock('@shared/utils')
      .getCurrentLocale as jest.Mock;
    mockGetDifferenceInDays = jest.requireMock('@shared/utils')
      .getDifferenceInDays as jest.Mock;
    mockGetFormattedDate = jest.requireMock('@shared/utils')
      .getFormattedDate as jest.Mock;
    mockGetTimeFromUlid = jest.requireMock('@shared/utils')
      .getTimeFromUlid as jest.Mock;

    mockIsToday.mockReset();
    mockIsYesterday.mockReset();
    mockTranslate.mockReset();
    mockCapitalize.mockReset();
    mockFormat.mockReset();
    mockGetCurrentLocale.mockReset();
    mockGetDifferenceInDays.mockReset();
    mockGetFormattedDate.mockReset();
    mockGetTimeFromUlid.mockReset();

    mockGetCurrentLocale.mockReturnValue(locale);
    mockCapitalize.mockImplementation(value => value);

    jest.isolateModules(() => {
      // eslint-disable-next-line @typescript-eslint/no-var-requires
      const dateUtils = require('../date') as typeof import('../date');
      ({getHumanizedDateWithTime, getLocalizedDateLabelByTs} = dateUtils);
    });
  });

  it('returns today label for current day timestamps', () => {
    mockGetTimeFromUlid.mockReturnValue(date);
    mockIsToday.mockReturnValue(true);
    mockIsYesterday.mockReturnValue(false);
    mockTranslate.mockReturnValue('today');

    const label = getLocalizedDateLabelByTs('01HX1234');

    expect(label).toBe('today');
    expect(mockTranslate).toHaveBeenCalledWith('general.today');
    expect(mockGetFormattedDate).not.toHaveBeenCalled();
  });

  it('returns yesterday label for previous day timestamps', () => {
    mockGetTimeFromUlid.mockReturnValue(date);
    mockIsToday.mockReturnValue(false);
    mockIsYesterday.mockReturnValue(true);
    mockTranslate.mockReturnValue('yesterday');

    const label = getLocalizedDateLabelByTs('01HX2345');

    expect(label).toBe('yesterday');
    expect(mockTranslate).toHaveBeenCalledWith('general.yesterday');
  });

  it('returns weekday label for dates within the last week', () => {
    mockGetTimeFromUlid.mockReturnValue(date);
    mockIsToday.mockReturnValue(false);
    mockIsYesterday.mockReturnValue(false);
    mockGetDifferenceInDays.mockReturnValue(3);
    mockGetFormattedDate.mockReturnValue('monday');

    const label = getLocalizedDateLabelByTs('01HX3456');

    expect(mockGetFormattedDate).toHaveBeenCalledWith(date, 'EEEE', {
      locale,
    });
    expect(label).toBe('monday');
  });

  it('returns short date label for older dates', () => {
    mockGetTimeFromUlid.mockReturnValue(date);
    mockIsToday.mockReturnValue(false);
    mockIsYesterday.mockReturnValue(false);
    mockGetDifferenceInDays.mockReturnValue(10);
    mockGetFormattedDate.mockReturnValue('Mon, Jan 20');

    const label = getLocalizedDateLabelByTs('01HX4567');

    expect(mockGetFormattedDate).toHaveBeenCalledWith(date, 'EEE, LLL dd', {
      locale,
    });
    expect(label).toBe('Mon, Jan 20');
  });

  it('humanizes date and time for today', () => {
    mockIsToday.mockReturnValue(true);
    mockIsYesterday.mockReturnValue(false);
    mockTranslate.mockReturnValue('today');
    mockFormat.mockReturnValue('10:30');

    const result = getHumanizedDateWithTime(date);

    expect(result).toEqual({date: 'today', time: '10:30'});
    expect(mockFormat).toHaveBeenCalledWith(date, 'HH:mm', {locale});
  });

  it('humanizes date for older timestamps', () => {
    mockIsToday.mockReturnValue(false);
    mockIsYesterday.mockReturnValue(false);
    mockFormat.mockReturnValue('10:30');
    mockGetFormattedDate.mockReturnValue('20/01/2024');

    const result = getHumanizedDateWithTime(date);

    expect(mockGetFormattedDate).toHaveBeenCalledWith(date, 'dd/MM/yyyy', {
      locale,
    });
    expect(result).toEqual({date: '20/01/2024', time: '10:30'});
  });
});
