import { useCallback, useRef } from 'react';
import { useMutation } from 'react-query';

import { type AxiosError } from 'axios';

import useHuSnackbar from '@material-hu/components/design-system/Snackbar';

import { logEvent } from 'src/config/amplitude';
import useGeneralError from 'src/hooks/useGeneralError';
import {
  copyAndPasteShifts,
  type ShiftCopyPasteParams,
  ShiftErrorCodes,
} from 'src/services/shifts';
import { EventName } from 'src/types/amplitude';
import { type ResponseError } from 'src/types/services';
import {
  type CopiedShiftCell,
  type ShiftAssignCellVC,
  type ShiftCalendarUser,
} from 'src/types/shifts';
import { useLokaliseTranslation } from 'src/utils/i18n';

import { useShiftsSelectionActions } from '../contexts/ShiftsSelectionContext';
import { invalidateShiftsCalendar } from '../queries';
import {
  buildPastePayload,
  buildPastePayloadsForPattern,
  calculateAffectedPasteCells,
  type ShiftFilters,
  validateCopiedCells,
  validatePasteOperation,
} from '../utils';
import { parsePasteTargetDate } from '../utils/copyPaste';
import {
  buildAttemptedCopiedData,
  buildShiftCopiedEventProperties,
  buildShiftPastedEventProperties,
} from '../utils/track';
import { ShiftManagementMenuSource } from 'src/types/amplitude';

const buildSourceProperties = (source?: ShiftManagementMenuSource) => ({
  use: source,
  from_keyboard: source === ShiftManagementMenuSource.KEYBOARD,
});

type Props = {
  registers: ShiftCalendarUser[];
  filters: ShiftFilters;
  days?: { key: string }[];
};

export const useShiftCopyPaste = ({ registers, filters, days }: Props) => {
  const { t } = useLokaliseTranslation('shifts');
  const showGeneralError = useGeneralError();
  const { enqueueSnackbar } = useHuSnackbar();
  const pendingPatternPastesRef = useRef(0);
  const isPatternBatchRef = useRef(false);
  const pendingPasteEventRef = useRef<Record<string, unknown> | null>(null);
  const pasteSourceRef = useRef<ShiftManagementMenuSource | undefined>(
    undefined,
  );
  const {
    isAdvancedMode,
    selectedCells,
    selectedDates,
    selectedShifts,
    copiedCells,
    allRowsSelected,
    excludeCells,
    setCopiedCells,
    setPastingCells,
    clearAllSelections,
  } = useShiftsSelectionActions();

  const executeCopy = useCallback(
    (
      standaloneShift?: {
        shiftId: number;
        userId: number;
        date: string;
      } | null,
      source?: ShiftManagementMenuSource,
    ) => {
      // If shift is provided, create a temporary selection with it
      const shiftsToValidate = standaloneShift
        ? new Map([[standaloneShift.shiftId, standaloneShift]])
        : selectedShifts;

      const copyResult = validateCopiedCells({
        registers: registers || [],
        selectedCells,
        selectedShifts: shiftsToValidate,
        areDatesSelected: selectedDates.size > 0,
      });

      if (!copyResult.success && copyResult.errorMessage) {
        if (days?.length) {
          const attemptedData = buildAttemptedCopiedData(
            selectedCells,
            shiftsToValidate,
            registers || [],
          );
          logEvent(EventName.SHIFT_MANAGEMENT_SHIFT_COPIED, {
            ...buildShiftCopiedEventProperties(attemptedData, days),
            copy_valid: false,
            ...buildSourceProperties(source),
          });
        }
        enqueueSnackbar({
          title: t(copyResult.errorMessage),
          description: copyResult.errorDescription
            ? t(copyResult.errorDescription)
            : undefined,
          variant: 'info',
          hasClose: true,
        });
        return;
      }
      if (!copyResult.copiedData) return;
      setCopiedCells(copyResult.copiedData);
      if (days?.length) {
        logEvent(EventName.SHIFT_MANAGEMENT_SHIFT_COPIED, {
          ...buildShiftCopiedEventProperties(copyResult.copiedData, days),
          copy_valid: true,
          ...buildSourceProperties(source),
        });
      }
    },
    [
      registers,
      selectedCells,
      selectedShifts,
      selectedDates,
      setCopiedCells,
      enqueueSnackbar,
      t,
      days,
    ],
  );

  const {
    mutate: pasteShiftMutation,
    mutateAsync: pasteShiftMutationAsync,
    isLoading: isPastingShifts,
  } = useMutation({
    mutationFn: (data: ShiftCopyPasteParams) => copyAndPasteShifts(data),
    onSuccess: async () => {
      if (!isPatternBatchRef.current && pendingPasteEventRef.current) {
        logEvent(
          EventName.SHIFT_MANAGEMENT_SHIFT_PASTED,
          pendingPasteEventRef.current,
        );
        pendingPasteEventRef.current = null;
      }
      if (isPatternBatchRef.current) return;
      await invalidateShiftsCalendar();
      enqueueSnackbar({
        title: t('shifts:actions.assign.success'),
        variant: 'success',
        hasClose: true,
      });
      clearAllSelections({
        keepCopiedCells: true,
        keepAdvancedMode: true,
        keepSelectedShifts: !isAdvancedMode,
      });
    },
    onError: (error: AxiosError<ResponseError>) => {
      // If overlap error show info snackbar
      if (
        error.response?.data.code ===
          ShiftErrorCodes.SHIFT_MANAGEMENT_REST_SHIFT_OVERLAP ||
        error.response?.data.code ===
          ShiftErrorCodes.SHIFT_MANAGEMENT_INVALID_TIME_SLOTS
      ) {
        enqueueSnackbar({
          title: t('shifts:actions.paste.error_conflict'),
          description: t('shifts:actions.paste.error_conflict_description'),
          variant: 'info',
          hasClose: true,
        });
      } else {
        showGeneralError(error, t('shifts:actions.assign.error'), true, {
          description: t('shifts:actions.assign.error_description'),
        });
      }
    },
    onSettled: () => {
      pendingPatternPastesRef.current = Math.max(
        0,
        pendingPatternPastesRef.current - 1,
      );
      if (pendingPatternPastesRef.current <= 0) {
        if (pendingPasteEventRef.current) {
          logEvent(
            EventName.SHIFT_MANAGEMENT_SHIFT_PASTED,
            pendingPasteEventRef.current,
          );
          pendingPasteEventRef.current = null;
        }
        setPastingCells(new Set());
      }
    },
  });

  const executePaste = useCallback(
    (targetCell?: ShiftAssignCellVC, source?: ShiftManagementMenuSource) => {
      pasteSourceRef.current = source;
      // Don't allow pasting if another pasting is in progress
      if (isPastingShifts) return;
      // If cell is provided, use it; otherwise use selected cells
      const cellsToValidate = targetCell ? [targetCell] : selectedCells;

      const pasteValidation = validatePasteOperation({
        copiedCells,
        selectedCells: cellsToValidate,
        selectedDates,
        allRowsSelected,
        registers: registers || [],
      });

      if (!pasteValidation.success && pasteValidation.errorMessage) {
        enqueueSnackbar({
          title: t(pasteValidation.errorMessage),
          variant: 'info',
          hasClose: true,
        });
        return;
      }
      if (!pasteValidation.pasteTargets) return;

      const hasDateSelections = selectedDates.size > 0 || allRowsSelected;
      const isPatternMode =
        !hasDateSelections &&
        !!pasteValidation.targetCells &&
        pasteValidation.targetCells.length > 0;

      const copiedCellKeys = new Set<string>();
      copiedCells.forEach(cell => {
        copiedCellKeys.add(`${cell.userId}-${cell.date}`);
      });
      const excludedCellKeys = new Set<string>();
      excludeCells.forEach(cell => {
        excludedCellKeys.add(`${cell.userId}-${cell.date}`);
      });

      if (isPatternMode && pasteValidation.targetCells) {
        // Multiple destinations: pattern (c1, c2, c1, c2...). Single destination: full list (c1..cN)
        const { payloads, targetCells: effectiveTargetCells } =
          buildPastePayloadsForPattern({
            copiedCells,
            targetCells: pasteValidation.targetCells,
            registers: registers || [],
          });
        if (payloads.length === 0) return;

        if (days?.length) {
          const pastedCells: CopiedShiftCell[] = effectiveTargetCells.map(
            c => ({ userId: c.userId, date: c.date, shiftIds: [] }),
          );
          pendingPasteEventRef.current = {
            ...buildShiftPastedEventProperties(pastedCells, days),
            ...buildSourceProperties(pasteSourceRef.current),
          };
        }
        const pastingCellKeys = new Set<string>();
        effectiveTargetCells.forEach(cell => {
          const cellKey = `${cell.userId}-${cell.date}`;
          if (!copiedCellKeys.has(cellKey) && !excludedCellKeys.has(cellKey)) {
            pastingCellKeys.add(cellKey);
          }
        });
        setPastingCells(pastingCellKeys);
        pendingPatternPastesRef.current = payloads.length;
        isPatternBatchRef.current = true;
        (async () => {
          try {
            for (const payload of payloads) {
              await pasteShiftMutationAsync(payload);
            }
            await invalidateShiftsCalendar();
            enqueueSnackbar({
              title: t('shifts:actions.assign.success'),
              variant: 'success',
              hasClose: true,
            });
            clearAllSelections({
              keepCopiedCells: true,
              keepAdvancedMode: true,
              keepSelectedShifts: !isAdvancedMode,
            });
          } catch {
            // Error already handled in onError
          } finally {
            isPatternBatchRef.current = false;
          }
        })();
        return;
      }

      const payload = buildPastePayload({
        copiedCells,
        pasteTargets: pasteValidation.pasteTargets,
        registers: registers || [],
        selectedDates,
        allRowsSelected,
        excludeCells,
        filters,
      });

      const sortedDates =
        selectedDates.size > 1 ? [...selectedDates].sort() : null;
      const multipleDatePastes =
        hasDateSelections &&
        copiedCells.length === 1 &&
        sortedDates &&
        sortedDates.length > 1;

      if (multipleDatePastes && payload.paste) {
        if (days?.length && sortedDates) {
          const pastedCells: CopiedShiftCell[] = (registers || []).flatMap(
            user =>
              sortedDates.map(date => ({
                userId: user.userId,
                date,
                shiftIds: [] as number[],
              })),
          );
          pendingPasteEventRef.current = {
            ...buildShiftPastedEventProperties(pastedCells, days),
            ...buildSourceProperties(pasteSourceRef.current),
          };
        }
        const payloads = sortedDates.map(date => ({
          copy: payload.copy,
          paste: {
            ...payload.paste,
            startDate: parsePasteTargetDate(date, copiedCells),
          },
        }));
        const pastingCellKeys = new Set<string>();
        (registers || []).forEach(user => {
          sortedDates.forEach(date => {
            const cellKey = `${user.userId}-${date}`;
            if (!copiedCellKeys.has(cellKey) && !excludedCellKeys.has(cellKey))
              pastingCellKeys.add(cellKey);
          });
        });
        setPastingCells(pastingCellKeys);
        pendingPatternPastesRef.current = payloads.length;
        isPatternBatchRef.current = true;
        (async () => {
          try {
            for (const p of payloads) {
              await pasteShiftMutationAsync(p);
            }
            await invalidateShiftsCalendar();
            enqueueSnackbar({
              title: t('shifts:actions.assign.success'),
              variant: 'success',
              hasClose: true,
            });
            clearAllSelections({
              keepCopiedCells: true,
              keepAdvancedMode: true,
              keepSelectedShifts: !isAdvancedMode,
            });
          } catch {
            // already handled in onError
          } finally {
            isPatternBatchRef.current = false;
          }
        })();
        return;
      }

      const affectedCells = calculateAffectedPasteCells(
        copiedCells,
        pasteValidation.pasteTargets,
      );

      if (days?.length) {
        const pastedCells: CopiedShiftCell[] = affectedCells.map(c => ({
          userId: c.userId,
          date: c.date,
          shiftIds: [],
        }));
        pendingPasteEventRef.current = {
          ...buildShiftPastedEventProperties(pastedCells, days),
          ...buildSourceProperties(pasteSourceRef.current),
        };
      }
      const pastingCellKeys = new Set<string>();
      affectedCells.forEach(cell => {
        const cellKey = `${cell.userId}-${cell.date}`;
        if (!copiedCellKeys.has(cellKey) && !excludedCellKeys.has(cellKey)) {
          pastingCellKeys.add(cellKey);
        }
      });
      setPastingCells(pastingCellKeys);
      pendingPatternPastesRef.current = 1;
      pasteShiftMutation(payload);
    },
    [
      copiedCells,
      selectedCells,
      selectedDates,
      allRowsSelected,
      excludeCells,
      isPastingShifts,
      filters,
      registers,
      enqueueSnackbar,
      setPastingCells,
      pasteShiftMutation,
      pasteShiftMutationAsync,
      clearAllSelections,
      isAdvancedMode,
      t,
      days,
    ],
  );

  return {
    executeCopy,
    executePaste,
  };
};
