import React from 'react';
import { render } from '@testing-library/react';
import StringDateTimePickerGroup from './StringDateTimePickerGroup';
import FormTestBase, { PersonDto } from '../__Tests__/FormTestBase';

//hack so the datetimepicker internals don't complain about this not existing in the context of jest
HTMLCanvasElement.prototype.getContext = () => null;

describe('StringDateTimePickerGroup', () => {
  it('renders without crashing', () => {
    render(
      <FormTestBase>
        {({ Field }) => (
          <Field
            name="createdDateTime"
            Component={StringDateTimePickerGroup}
            label="Created Date Time"
            monthPlaceholder="mm"
            dayPlaceholder="dd"
            yearPlaceholder="yyyy"
            disableClock
            maxDate={new Date('9/23/2023')}
            minDate={new Date('6/22/2022')}
          />
        )}
      </FormTestBase>
    );
  });

  it('has matching snapshot', () => {
    const renderResult = render(
      <FormTestBase>
        {({ Field }) => (
          <Field
            name="createdDateTime"
            Component={StringDateTimePickerGroup}
            label="Created Date Time"
            monthPlaceholder="mm"
            dayPlaceholder="dd"
            yearPlaceholder="yyyy"
            disableClock
            maxDate={new Date('9/23/2023')}
            minDate={new Date('6/22/2022')}
          />
        )}
      </FormTestBase>
    );
    expect(renderResult.asFragment()).toMatchSnapshot();
  });

  it('does not render with initial date-only string and throws exception', () => {
    const dateOnlyString = '2024-09-04';
    const personDtoWithDateOnlyString: PersonDto = {
      createdDateTime: dateOnlyString,
    };

    try {
      render(
        <FormTestBase initialValues={personDtoWithDateOnlyString}>
          {({ Field }) => (
            <Field
              name="createdDateTime"
              Component={StringDateTimePickerGroup}
              label="Created Date Time"
              monthPlaceholder="mm"
              dayPlaceholder="dd"
              yearPlaceholder="yyyy"
              disableClock
            />
          )}
        </FormTestBase>
      );

      fail('Expected an exception to be thrown for date-only string');
    } catch (error: any) {
      // Assert that the error message matches the expected message
      expect(error.message).toEqual(
        `Invalid "date time" value of ${dateOnlyString} provided to DateTimePickerGroup component. Please provide ISO 8601 string that includes the time zone designator. Sample strings: 2022-09-24T:08:54:12+00:00, 2022-09-24T:08:54:12Z, 2022-09-24T:08:54:12.123-05:00`
      );
    }
  });

  it('renders with ISO UTC date time string taking browser timezone into account', () => {
    const isoUTCDateTimeString = '2024-04-09T21:00:24.670Z';
    const personDtoWithDateTimeString: PersonDto = {
      createdDateTime: isoUTCDateTimeString,
    };

    render(
      <FormTestBase initialValues={personDtoWithDateTimeString}>
        {({ Field }) => (
          <Field
            name="createdDateTime"
            Component={StringDateTimePickerGroup}
            label="Created Date Time"
            monthPlaceholder="mm"
            dayPlaceholder="dd"
            yearPlaceholder="yyyy"
            disableClock
          />
        )}
      </FormTestBase>
    );

    const dateTimePickerInputComponent = document.querySelector(
      'input[name="datetime"][type="datetime-local"]'
    );
    const utcDate = new Date(isoUTCDateTimeString);
    const localDateTimeString =
      convertToDateTimePickerComponentValueString(utcDate);

    // Assert that no exception is thrown during rendering
    expect(true).toBe(true);
    // Assert the value of the component
    expect(dateTimePickerInputComponent).toHaveValue(localDateTimeString);
  });

  it('does not render with invalid datetime ISO string and throws exception', () => {
    const invalidISODateTimeString = '2024-04-11T01:00:00.000-5:00';
    const personDtoWithDateOnlyString: PersonDto = {
      createdDateTime: invalidISODateTimeString,
    };

    try {
      render(
        <FormTestBase initialValues={personDtoWithDateOnlyString}>
          {({ Field }) => (
            <Field
              name="createdDateTime"
              Component={StringDateTimePickerGroup}
              label="Created Date Time"
              monthPlaceholder="mm"
              dayPlaceholder="dd"
              yearPlaceholder="yyyy"
              disableClock
            />
          )}
        </FormTestBase>
      );

      fail('Expected an exception to be thrown for invalid datetime string');
    } catch (error: any) {
      // Assert that the error message matches the expected message
      expect(error.message).toEqual(
        `Invalid "date time" value of ${invalidISODateTimeString} provided to DateTimePickerGroup component. Please provide ISO 8601 string that includes the time zone designator. Sample strings: 2022-09-24T:08:54:12+00:00, 2022-09-24T:08:54:12Z, 2022-09-24T:08:54:12.123-05:00`
      );
    }
  });

  it('renders with ISO date time string with different offset than browser taking browser timezone into account', () => {
    const isoDateTimeString = '2024-04-09T21:00:24.670-01:00';
    const personDtoWithDateTimeString: PersonDto = {
      createdDateTime: isoDateTimeString,
    };

    render(
      <FormTestBase initialValues={personDtoWithDateTimeString}>
        {({ Field }) => (
          <Field
            name="createdDateTime"
            Component={StringDateTimePickerGroup}
            label="Created Date Time"
            monthPlaceholder="mm"
            dayPlaceholder="dd"
            yearPlaceholder="yyyy"
            disableClock
          />
        )}
      </FormTestBase>
    );

    const dateTimePickerInputComponent = document.querySelector(
      'input[name="datetime"][type="datetime-local"]'
    );
    const utcDate = new Date(isoDateTimeString);
    const localDateTimeString =
      convertToDateTimePickerComponentValueString(utcDate);

    // Assert that no exception is thrown during rendering
    expect(true).toBe(true);
    // Assert the value of the component
    expect(dateTimePickerInputComponent).toHaveValue(localDateTimeString);
  });

  it('renders with ISO date time string with same offset as the browser taking browser timezone into account', () => {
    const isoDateTimeString = '2024-04-09T21:00:24.670-05:00';
    const personDtoWithDateTimeString: PersonDto = {
      createdDateTime: isoDateTimeString,
    };

    render(
      <FormTestBase initialValues={personDtoWithDateTimeString}>
        {({ Field }) => (
          <Field
            name="createdDateTime"
            Component={StringDateTimePickerGroup}
            label="Created Date Time"
            monthPlaceholder="mm"
            dayPlaceholder="dd"
            yearPlaceholder="yyyy"
            disableClock
          />
        )}
      </FormTestBase>
    );

    const dateTimePickerInputComponent = document.querySelector(
      'input[name="datetime"][type="datetime-local"]'
    );
    const utcDate = new Date(isoDateTimeString);
    const localDateTimeString =
      convertToDateTimePickerComponentValueString(utcDate);

    // Assert that no exception is thrown during rendering
    expect(true).toBe(true);
    // Assert the value of the component
    expect(dateTimePickerInputComponent).toHaveValue(localDateTimeString);
  });
});

function convertToDateTimePickerComponentValueString(date: Date): string {
  // Format the date in the required format using `intl.DateTimeFormat`
  const year = new Intl.DateTimeFormat('en', { year: 'numeric' })
    .format(date)
    .padStart(4, '0');

  const month = new Intl.DateTimeFormat('en', { month: '2-digit' }).format(
    date
  );

  const day = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(date);

  const hour = new Intl.DateTimeFormat('en', {
    hour: '2-digit',
    hourCycle: 'h23',
  }).format(date);

  const minute = new Intl.DateTimeFormat('en', { minute: '2-digit' })
    .format(date)
    .padStart(2, '0');

  return `${year}-${month}-${day}T${hour}:${minute}`;
}
