import { useEffect, useMemo, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useMutation, useQuery } from 'react-query';
import { useParams } from 'react-router-dom';

import { xor } from 'lodash-es';

import { type AutocompleteOption } from '@material-hu/components/design-system/Inputs/Autocomplete/types';
import useSnackbar from '@material-hu/components/design-system/Snackbar';

import { queryClient } from 'src/config/react-query';
import { useAuth } from 'src/contexts/JWTContext';
import {
  getPeerReviewersSelected,
  getReviewCycle,
  updatePeerReviewersSelected,
} from 'src/pages/dashboard/performance/services';
import {
  ReviewDirection,
  type ReviewerUserSelection,
} from 'src/pages/dashboard/performance/types';
import { insertIf } from 'src/utils/arrays';
import { useLokaliseTranslation } from 'src/utils/i18n';

import { performanceKeys } from '../../../queries';

import { type FinalSelectionUser, hasSelectReviewType } from './utils';

type UsePeerSelectComponentParams = {
  reviewedId: number;
  reviewerCount: number;
};

export const usePeerSelectComponent = ({
  reviewedId,
  reviewerCount,
}: UsePeerSelectComponentParams) => {
  const { id } = useParams();
  const { t } = useLokaliseTranslation('performance');
  const { enqueueSnackbar } = useSnackbar();
  const { user: loggedUser } = useAuth();

  const [open, setOpen] = useState(false);
  const [finalSelection, setFinalSelection] = useState<FinalSelectionUser[]>(
    [],
  );
  const [reviewType, setReviewType] = useState<ReviewDirection | null>(null);
  const [isNewExternal, setIsNewExternal] = useState(false);

  const isExternal = reviewType === ReviewDirection.PEER_EXTERNAL_REVIEW;

  const form = useForm({
    defaultValues: {
      reviewer: null,
      lastName: '',
      email: '',
    },
  });

  const reviewer = form.watch('reviewer') as
    | FinalSelectionUser
    | AutocompleteOption
    | null;

  const handleToggle = (type: ReviewDirection | null = null) => {
    setReviewType(type);
    setOpen(prev => !prev);
    setIsNewExternal(false);
  };

  const handleReset = () => {
    setFinalSelection([]);
    setOpen(false);
  };

  const handleGoBack = () => {
    setIsNewExternal(false);
  };

  const handleRemoveUser = (user: FinalSelectionUser) =>
    setFinalSelection(finalSelection.filter(u => u.id !== user.id));

  const { data: cycle } = useQuery(
    performanceKeys.cycle(id),
    () => getReviewCycle(id ?? ''),
    {
      select: response => response.data,
    },
  );

  const {
    data: reviewers,
    refetch: refetchPeer,
    isLoading,
  } = useQuery(
    performanceKeys.reviewers(id, reviewedId),
    () =>
      getPeerReviewersSelected(
        id ?? '',
        reviewedId,
        reviewType ?? ReviewDirection.PEER_REVIEW,
      ),
    {
      select: response => response.data,
      onSuccess: response => {
        const reviewersMapped = response.map(r => ({ ...r, id: r.userId }));
        setFinalSelection(reviewersMapped);
      },
      enabled:
        !!id && !!reviewedId && reviewerCount > 0 && open && !!reviewType,
    },
  );

  const { mutate, isLoading: isLoadingUpdateReviewersSelected } = useMutation(
    (reviewerIds: number[]) => {
      const currentReviewerIds =
        reviewers?.map((r: ReviewerUserSelection) => r.userId) || [];
      const currentReviewerIdsSet = new Set(currentReviewerIds);
      const newReviewerIdsSet = new Set(reviewerIds);

      const reviewerIdsToAdd = reviewerIds.filter(
        (reviewerId: number) => !currentReviewerIdsSet.has(reviewerId),
      );
      const reviewerIdsToDelete = currentReviewerIds.filter(
        (reviewerId: number) => !newReviewerIdsSet.has(reviewerId),
      );

      return updatePeerReviewersSelected({
        cycleId: parseInt(id ?? ''),
        userId: reviewedId,
        evaluationType: reviewType ?? ReviewDirection.PEER_REVIEW,
        reviewerIdsToAdd: reviewerIdsToAdd.length
          ? reviewerIdsToAdd
          : undefined,
        reviewerIdsToDelete: reviewerIdsToDelete.length
          ? reviewerIdsToDelete
          : undefined,
      });
    },
    {
      onSuccess: () => {
        queryClient.invalidateQueries(performanceKeys.revieweds(id));
        handleToggle(reviewType);
        enqueueSnackbar({
          title: t('form.update_reviewers_success'),
          variant: 'success',
        });
      },
    },
  );

  const handleApply = () => {
    mutate(finalSelection.map(u => u.id));
  };

  const excludedIds = useMemo(
    () =>
      new Set([...finalSelection.map(s => s.id), reviewedId, loggedUser?.id]),
    [finalSelection, reviewedId, loggedUser?.id],
  );

  const filter = <T extends { id?: number }>(options: T[]): T[] =>
    options.filter((u: T) => !u.id || !excludedIds.has(u.id));

  // biome-ignore lint/correctness/useExhaustiveDependencies: Run only when reviewer changes
  useEffect(() => {
    if (!reviewer) return;

    const externalOption = reviewer as AutocompleteOption;
    const selected: FinalSelectionUser = isExternal
      ? {
          id: externalOption.value as number,
          firstName: externalOption.label,
          lastName: '',
          profilePicture: null,
          userId: externalOption.value as number,
          isExternal: true,
        }
      : (reviewer as FinalSelectionUser);

    if (!finalSelection.some(u => u.id === selected.id)) {
      setFinalSelection(finalSelection.concat(selected));
      form.reset();
    }
  }, [reviewer]);

  // biome-ignore lint/correctness/useExhaustiveDependencies: reset form values when closing the drawer
  useEffect(() => {
    form.reset();
    if (open) refetchPeer();
  }, [open]);

  const directions = useMemo(
    () => [
      ...insertIf(
        hasSelectReviewType(cycle, ReviewDirection.PEER_REVIEW) ?? false,
        ReviewDirection.PEER_REVIEW,
      ),
      ...insertIf(
        hasSelectReviewType(cycle, ReviewDirection.PEER_EXTERNAL_REVIEW) ??
          false,
        ReviewDirection.PEER_EXTERNAL_REVIEW,
      ),
      ...insertIf(
        hasSelectReviewType(cycle, ReviewDirection.OTHERS_REVIEW) ?? false,
        ReviewDirection.OTHERS_REVIEW,
      ),
    ],
    [cycle],
  );

  const isDisabled =
    xor(
      reviewers?.map(r => r.userId),
      finalSelection?.map(f => f.id),
    )?.length === 0;

  return {
    form,
    directions,
    open,
    isNewExternal,
    isExternal,
    reviewType,
    finalSelection,
    isLoading,
    isLoadingUpdateReviewersSelected,
    isDisabled,
    filter,
    setIsNewExternal,
    handleToggle,
    handleReset,
    handleGoBack,
    handleApply,
    handleRemoveUser,
  };
};
