import { type FC, useEffect } from 'react';
import { useFormContext } from 'react-hook-form';
import { useParams } from 'react-router-dom';

import { IconMapPin } from '@material-hu/icons/tabler';
import Stack from '@material-hu/mui/Stack';

import HuFormTimePicker from '@material-hu/components/composed-components/TimePicker/form';
import Button from '@material-hu/components/design-system/Buttons/Button';
import HuFormInputClassic from '@material-hu/components/design-system/Inputs/Classic/form';
import HuFormInputSelect from '@material-hu/components/design-system/Inputs/Select/form';
import HuTooltip from '@material-hu/components/design-system/Tooltip';

import { useAuth } from 'src/contexts/JWTContext';
import useRules from 'src/hooks/useRules';
import {
  REMOTE_WORK_SITE_VALUE,
  TOOLTIP_DELAY,
} from 'src/pages/dashboard/timeTracking/constants';
import { getQueriedUserSites } from 'src/pages/dashboard/timeTracking/queries';
import {
  validateDateOrder,
  validateDatesCollision,
  validateNoEnclosedCycles,
  validateNoEqualDates,
  validateNoFutureEntries,
  validateNoRepeatedSameTypeEntry,
} from 'src/pages/dashboard/timeTracking/validations';
import {
  EntrySource,
  EntryType,
  type SiteCoordinates,
  type SiteLocation,
  type TimePairs,
  type UserSite,
} from 'src/types/timeTracking';
import { useLokaliseTranslation } from 'src/utils/i18n';

type Props = {
  type: EntryType;
  reservedCycles: TimePairs[];
  summaryDay: string;
  pairId?: string;
  kioskData: { id: number; name: string } | undefined;
  enableNightShift: boolean;
  userId: number;
  entryLocation?: SiteCoordinates | null;
  entrySource?: EntrySource;
  enableRemoteWork?: boolean;
  onViewMap?: (
    siteId: number,
    siteName: string,
    location: SiteLocation,
    markingLocation: SiteCoordinates | null,
    entrySource: EntrySource,
    entryType: EntryType,
  ) => void;
};

const EditEntry: FC<Props> = props => {
  const {
    kioskData,
    pairId,
    reservedCycles,
    summaryDay,
    type,
    enableNightShift,
    userId,
    entryLocation,
    entrySource,
    enableRemoteWork,
    onViewMap,
  } = props;
  const { t } = useLokaliseTranslation([
    'time_tracker',
    'general',
    'regions_sites',
  ]);
  const { id: paramsUserId } = useParams();
  const {
    trigger,
    watch,
    formState: { isSubmitted },
  } = useFormContext();
  const { instance } = useAuth();

  const KIOSK_OPTIONS = kioskData
    ? [{ value: kioskData.id, label: kioskData.name }]
    : [];
  const { data: sites = [] } =
    getQueriedUserSites(Number(paramsUserId) || userId) || {};
  const { start, end } = watch();
  const includeRemoteWorkOption =
    enableRemoteWork ||
    (type === EntryType.START
      ? start?.siteId === REMOTE_WORK_SITE_VALUE
      : end?.siteId === REMOTE_WORK_SITE_VALUE);
  const SITES_OPTIONS = [
    { value: -1, label: t('no_site') },
    ...(includeRemoteWorkOption
      ? [
          {
            value: REMOTE_WORK_SITE_VALUE,
            label: t('time_tracker:policies.remote_work'),
          },
        ]
      : []),
    ...sites.map((s: UserSite) => ({ value: s.id, label: s.name })),
  ];
  // Need to revalidate the form in the case that a user tries to fix the error by changing the valid field
  // because in that case the form will keep showing the error, until the non valid input is changed
  useEffect(() => {
    if (isSubmitted && (start.time || end.time)) {
      trigger('start.time');
      trigger('end.time');
    }
  }, [start.time, end.time, isSubmitted]);

  const fieldPrefix = type.toLowerCase();
  const fieldName = kioskData
    ? `${fieldPrefix}.kioskId`
    : `${fieldPrefix}.siteId`;
  // We filter in order to avoid comparing with the same pair
  const otherCycles = reservedCycles.filter(p => p.id !== pairId);
  const entryWithNightShift =
    enableNightShift && end.time && start.time && end.time < start.time;

  // Create rules for time picker
  const getTimeRules = () => {
    const validateRules = {
      required: type === EntryType.START,
      checkValidFormatDate: true,
    };
    const customRules = {
      validateDateOrder: (value: Date) =>
        validateDateOrder({
          date: value,
          dateToCompare: type === EntryType.START ? end.time : start.time,
          referenceDate: `${summaryDay}T00:00:00`,
          order: type === EntryType.START ? 'before' : 'after',
          enableNightShift,
        }),
      noRepeated: (value: Date) =>
        validateNoEqualDates(
          value,
          type === EntryType.START ? end.time : start.time,
        ),
      openCycleWithFutureEntries: (value: Date) =>
        validateNoEnclosedCycles({
          pairDates: {
            start: type === EntryType.START ? value : start.time,
            end: type === EntryType.START ? end.time : value,
          },
          referenceDate: `${summaryDay}T00:00:00`,
          reservedCycles: otherCycles,
          applyNightShift: entryWithNightShift,
        }),
      futureEntry: (value: Date) =>
        validateNoFutureEntries(
          value,
          `${summaryDay}T00:00:00`,
          instance?.timezone || '',
          type === EntryType.END && entryWithNightShift,
        ),
      sameTypeEntries: (value: Date) =>
        validateNoRepeatedSameTypeEntry(
          value,
          type,
          otherCycles,
          type === EntryType.END && entryWithNightShift,
        ),
      validateDatesCollision: (value: Date) =>
        validateDatesCollision(value, otherCycles, true, entryWithNightShift),
    };
    return useRules(validateRules, customRules);
  };

  return (
    <>
      <Stack sx={{ flexDirection: 'row', gap: 1, mb: 1 }}>
        <HuFormTimePicker
          name={`${fieldPrefix}.time`}
          inputProps={{
            label: type === EntryType.START ? t('entry') : t('exit'),
            placeholder: t('general:time_placeholder'),
            referenceDate: new Date(`${summaryDay}T00:00:00`),
            hideErrorText: true,
          }}
          rules={getTimeRules()}
        />
        <HuTooltip
          title={t('restricted_edit')}
          description={t('restricted_edit_description')}
          disableTooltip={!kioskData}
          direction="bottom"
          delay={TOOLTIP_DELAY}
        >
          {/* This stack is needed for the tooltip ref */}
          <Stack sx={{ width: '100%' }}>
            <HuFormInputSelect
              name={fieldName}
              rules={{ required: true }}
              inputProps={{
                label: kioskData ? t('terminal') : t('regions_sites:location'),
                options: kioskData ? KIOSK_OPTIONS : SITES_OPTIONS,
                disabled: Boolean(kioskData),
              }}
            />
          </Stack>
        </HuTooltip>
      </Stack>
      <HuFormInputClassic
        name={`${fieldPrefix}.comment`}
        inputProps={{
          placeholder: t('notes'),
          maxLength: 250,
        }}
      />
      {onViewMap && (
        <ViewMapButton
          siteId={watch(`${fieldPrefix}.siteId`)}
          sites={sites}
          entryLocation={entryLocation}
          entrySource={entrySource}
          entryType={type}
          onViewMap={onViewMap}
        />
      )}
    </>
  );
};

// Separate component to avoid re-renders
const ViewMapButton: FC<{
  siteId: number | undefined;
  sites: UserSite[];
  entryLocation?: SiteCoordinates | null;
  entrySource?: EntrySource;
  entryType: EntryType;
  onViewMap: (
    siteId: number,
    siteName: string,
    location: SiteLocation,
    markingLocation: SiteCoordinates | null,
    entrySource: EntrySource,
    entryType: EntryType,
  ) => void;
}> = ({ siteId, sites, entryLocation, entrySource, entryType, onViewMap }) => {
  const { t } = useLokaliseTranslation('time_tracker');

  const isRemoteWorkSelected = siteId === REMOTE_WORK_SITE_VALUE;
  const selectedSite =
    siteId && siteId !== -1 && !isRemoteWorkSelected
      ? sites.find(s => s.id === siteId)
      : null;

  const hasLocation = !!entryLocation;
  const isAppWithoutLocation = entrySource === EntrySource.APP && !hasLocation;

  const canShowMap = isRemoteWorkSelected
    ? hasLocation
    : selectedSite?.location && (hasLocation || isAppWithoutLocation);

  if (!canShowMap) return null;

  const handleClick = () => {
    if (isRemoteWorkSelected && entryLocation) {
      const remoteLocation: SiteLocation = {
        latitude: entryLocation.latitude,
        longitude: entryLocation.longitude,
        tolerance: 0,
        address: '',
      };
      onViewMap(
        -1,
        t('policies.remote_work'),
        remoteLocation,
        entryLocation,
        entrySource || EntrySource.APP,
        entryType,
      );
      return;
    }
    if (!selectedSite) return;
    onViewMap(
      selectedSite.id,
      selectedSite.name,
      selectedSite.location,
      entryLocation ?? null,
      entrySource || EntrySource.APP,
      entryType,
    );
  };

  return (
    <Button
      variant="secondary"
      startIcon={<IconMapPin size={16} />}
      onClick={handleClick}
      sx={{ alignSelf: 'flex-start' }}
    >
      {t('time_tracker:view_map')}
    </Button>
  );
};

export default EditEntry;
