import { useEffect, useState } from 'react';
import { useMutation, useQuery, useQueryClient } from 'react-query';
import { useParams } from 'react-router';

import { isEqual } from 'lodash-es';
import { useDrawer } from '@material-hu/hooks/useDrawer';
import { IconInfoSquareRounded, IconPlus } from '@material-hu/icons/tabler';
import Stack from '@material-hu/mui/Stack';
import Typography from '@material-hu/mui/Typography';
import { appearFromBottom, fadeIn } from '@material-hu/utils/animations';

import SortableList, {
  type RootProps,
  useSimpleSortableContainers,
} from '@material-hu/components/composed-components/SortableListComposition';
import StateCard from '@material-hu/components/composed-components/StateCard';
import Alert from '@material-hu/components/design-system/Alert';
import Button from '@material-hu/components/design-system/Buttons/Button';
import Spinner from '@material-hu/components/design-system/ProgressIndicators/Spinner';
import useSnackbar from '@material-hu/components/design-system/Snackbar';
import Title from '@material-hu/components/design-system/Title';

import useProfileFields from 'src/hooks/queryHooks/useProfileFields';
import StepLayout from 'src/pages/dashboard/EmployeeLifecycle/Process/components/StepLayout';
import { STEP_FORM_ID } from 'src/pages/dashboard/EmployeeLifecycle/Process/constants';
import { employeeLifecycleKeys } from 'src/pages/dashboard/EmployeeLifecycle/queries';
import {
  getProcessActions,
  getProcessSteps,
  reorderProcessActions,
} from 'src/pages/dashboard/EmployeeLifecycle/services';
import {
  type ActionType,
  type ProcessStepType,
} from 'src/pages/dashboard/EmployeeLifecycle/types';
import { useLokaliseTranslation } from 'src/utils/i18n';

import ProcessStepForm from '../../forms/ProcessStepForm';
import { convertProcessStepToFormValues } from '../../forms/ProcessStepForm/utils';
import { isCommonAssignee } from '../../forms/StepActionForms/shared/transforms';

import ProcessStepCard from './components/ProcessStepCard';
import { type ProcessStepsStepProps } from './types';

type StepWithIdAndActions = ProcessStepType & {
  id: string;
  actions: ActionWithId[];
};

type ActionWithId = ActionType & { id: string };

const ProcessStepsStep = ({
  isActiveOrPaused,
  onPreviousStep,
  onNextStep,
  processId: inheritedProcessId,
  sx,
  disabled,
}: ProcessStepsStepProps) => {
  const queryClient = useQueryClient();
  const { enqueueSnackbar } = useSnackbar();
  const { t } = useLokaliseTranslation(['employee_lifecycle', 'general']);
  const [steps, setSteps] = useState<StepWithIdAndActions[]>([]);
  const [initialSteps, setInitialSteps] = useState<StepWithIdAndActions[]>([]);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [expandedSteps, setExpandedSteps] = useState<Set<string>>(new Set());
  const { processId: paramsProcessId } = useParams();

  const processId = inheritedProcessId || paramsProcessId || '';

  const reorderProcessActionsMutation = useMutation({
    mutationFn: (data: {
      actionId: string;
      targetStepId: string;
      afterActionId: string;
    }) =>
      reorderProcessActions(
        processId,
        data.actionId,
        data.targetStepId,
        data.afterActionId,
      ),
    onError: () => {
      setSteps([...initialSteps]);
      enqueueSnackbar({
        variant: 'error',
        title: t('process_stepper.steps.process.steps.actions.reorder.error'),
      });
    },
    onSuccess: () => {
      enqueueSnackbar({
        variant: 'success',
        title: t('process_stepper.steps.process.steps.actions.reorder.success'),
      });
      queryClient.invalidateQueries(employeeLifecycleKeys.all());
    },
  });

  const stepsQuery = useQuery({
    queryKey: employeeLifecycleKeys.processSteps(processId),
    queryFn: () => getProcessSteps(processId),
    enabled: !!processId,
    keepPreviousData: true,
  });

  const actionsQuery = useQuery({
    queryKey: employeeLifecycleKeys.processActions(processId),
    queryFn: () => getProcessActions(processId),
    enabled: !!processId,
    keepPreviousData: true,
  });

  // TODO: This could be improved to only fetch the profile fields that are needed
  const profileFieldsQuery = useProfileFields();

  const {
    drawer: processStepDrawer,
    closeDrawer: closeProcessStepDrawer,
    showDrawer: showProcessStepDrawer,
  } = useDrawer(
    ProcessStepForm,
    {
      PaperProps: {
        sx: {
          width: '600px',
        },
      },
      disableEscapeKeyDown: true,
      onClose: () => {
        closeProcessStepDrawer();
      },
      title: t(
        paramsProcessId
          ? 'process_stepper.steps.process.steps.edit'
          : 'process_stepper.steps.process.steps.new',
      ),
      primaryButtonProps: {
        type: 'submit',
        form: STEP_FORM_ID,
        fullWidth: true,
        children: t('general:save'),
        loading: isSubmitting,
      },
      secondaryButtonProps: {
        children: t('general:cancel'),
        fullWidth: true,
        onClick: () => closeProcessStepDrawer(),
        disabled: isSubmitting,
      },
    },
    {
      disabled,
      onSubmitting: () => {
        setIsSubmitting(true);
      },
      onSubmitted: ({ values }) => {
        setIsSubmitting(false);
        setExpandedSteps(prev => {
          const newExpandedSteps = new Set(prev);
          newExpandedSteps.add(values.stepId || '');
          return newExpandedSteps;
        });
        closeProcessStepDrawer();
      },
      onError: () => {
        setIsSubmitting(false);
      },
      processId,
      formId: STEP_FORM_ID,
    },
  );

  const actionsByStepId = Object.groupBy(
    actionsQuery?.data || [],
    action => action.stepId,
  );

  const isLoading =
    stepsQuery.isLoading ||
    actionsQuery.isLoading ||
    profileFieldsQuery.isLoading;

  const canContinue =
    !!steps.length &&
    steps.every(step => !!step.actions.length) &&
    !reorderProcessActionsMutation.isLoading;

  const handleDragOver = useSimpleSortableContainers({
    containers: steps,
    setContainers: setSteps,
    itemsKey: 'actions',
  });

  const getAfterAction = ({
    actionId,
    stepId,
  }: {
    actionId: string;
    stepId: string;
  }) => {
    const stepActions =
      steps.find(step => step.stepId.toString() === stepId.toString())
        ?.actions || [];
    const actionIndex = stepActions.findIndex(
      action => action.id.toString() === actionId.toString(),
    );

    const afterActionIndex = actionIndex - 1 >= 0 ? actionIndex - 1 : null;
    const afterAction =
      afterActionIndex !== null ? stepActions[afterActionIndex] : null;

    return afterAction;
  };

  const handleDragEnd: RootProps['onDragEnd'] = event => {
    const { active, activeContainer } = event;
    const stepId = activeContainer?.split('-')[1];
    if (stepId) {
      const afterAction = getAfterAction({
        actionId: active as string,
        stepId,
      });

      const areInitialStepsEqual = isEqual(initialSteps, steps);

      if (areInitialStepsEqual) {
        return;
      }

      reorderProcessActionsMutation.mutate({
        actionId: active as string,
        targetStepId: stepId,
        afterActionId: afterAction?.actionId
          ? afterAction.actionId.toString()
          : '',
      });
    }
  };

  useEffect(() => {
    if (actionsQuery.data && stepsQuery.data) {
      const stepsWithActions = stepsQuery.data.map(step => ({
        id: step.stepId,
        ...step,
        actions: actionsQuery.data
          .filter(action => action.stepId === step.stepId)
          .map(action => ({
            ...action,
            id: action.actionId,
            assignee: isCommonAssignee(
              action.assignee as ActionType['assignee'],
            )
              ? action.assignee
              : (profileFieldsQuery?.data?.find(
                  field => field.id === action.assignee,
                )?.name as ActionType['assignee']),
          })),
      }));

      setSteps(stepsWithActions);
      setInitialSteps(stepsWithActions);
    }
  }, [
    actionsQuery.dataUpdatedAt,
    stepsQuery.dataUpdatedAt,
    profileFieldsQuery.dataUpdatedAt,
  ]);

  return (
    <StepLayout
      sx={sx}
      footer={
        <>
          <Button
            variant="tertiary"
            size="large"
            sx={{
              animation: `${fadeIn} 125ms ease-in-out backwards`,
            }}
            onClick={onPreviousStep}
          >
            {t('general:back')}
          </Button>
          <Button
            variant="primary"
            size="large"
            onClick={onNextStep}
            disabled={!canContinue}
            loading={isLoading}
          >
            {t('general:continue')}
          </Button>
        </>
      }
    >
      {processStepDrawer}
      <Stack sx={{ gap: 3, px: 2 }}>
        <Title
          title={t('process_stepper.steps.process.description')}
          variant="M"
        />
        {disabled && (
          <Alert
            title={t('process_stepper.edition_not_available')}
            description={t(
              'process_stepper.steps.process.edition_not_available_description',
            )}
            hasClose
            severity="warning"
          />
        )}
        {isActiveOrPaused && (
          <Alert
            title={t(
              'process_stepper.steps.process.active_or_paused_alert_title',
            )}
            description={t(
              'process_stepper.steps.process.active_or_paused_alert_description',
            )}
            hasClose
            severity="info"
          />
        )}
        {isLoading && (
          <Stack sx={{ alignItems: 'center', gap: 2, marginBottom: 5 }}>
            <Spinner centered />
            <Typography variant="globalS">
              {t('process_stepper.steps.process.steps.loading')}
            </Typography>
          </Stack>
        )}
        {!stepsQuery.data?.length && !isLoading && (
          <StateCard
            sx={{
              p: 1,
              '& .MuiCardContent-root': {
                display: 'flex',
                gap: 1,
                flexDirection: 'column',
                alignItems: 'center',
              },
            }}
            Icon={IconInfoSquareRounded}
            variant="L"
            title={t('process_stepper.steps.process.steps.list.empty.title')}
            description={t(
              'process_stepper.steps.process.steps.list.empty.description',
            )}
          />
        )}

        <SortableList.Root
          onDragOver={handleDragOver}
          dragByHandler={true}
          hasDragOverlay={true}
          onDragEnd={handleDragEnd}
          sx={{ gap: 2 }}
          isDraggable={() => true}
        >
          {!!stepsQuery.data?.length && !!steps.length && !isLoading && (
            <SortableList.Container id="container-steps">
              <Stack sx={{ gap: 2 }}>
                {steps.map((step, index) => (
                  <SortableList.Item
                    key={step.stepId}
                    id={`step-${step.stepId}`}
                  >
                    <ProcessStepCard
                      {...step}
                      actions={step.actions}
                      isExpanded={
                        expandedSteps.has(step.stepId) ||
                        !actionsByStepId[step.stepId]?.length
                      }
                      isLoadingActionsMutation={
                        reorderProcessActionsMutation.isLoading
                      }
                      processId={processId}
                      stepId={step.stepId}
                      onEdit={() => {
                        showProcessStepDrawer({
                          stepId: step.stepId,
                          processId,
                          defaultValues: convertProcessStepToFormValues(
                            step,
                            t,
                          ),
                          formId: STEP_FORM_ID,
                        });
                      }}
                      sx={{
                        animation: `${appearFromBottom} 125ms ease-in-out backwards`,
                        animationDelay: `${index * 25}ms`,
                        mb: 0,
                      }}
                      title={step.title}
                      trigger={step.trigger}
                      disabled={disabled}
                    />
                  </SortableList.Item>
                ))}
              </Stack>
            </SortableList.Container>
          )}
        </SortableList.Root>
        {!isLoading && (
          <Button
            variant="secondary"
            size="large"
            startIcon={<IconPlus size={16} />}
            onClick={() => showProcessStepDrawer()}
            disabled={isLoading || disabled}
            sx={{
              animation: `${appearFromBottom} 125ms ease-in-out backwards`,
              animationDelay: `${(steps.length + 1) * 25}ms`,
              mb: 0,
            }}
          >
            {t('process_stepper.steps.process.steps.new')}
          </Button>
        )}
      </Stack>
    </StepLayout>
  );
};

export default ProcessStepsStep;
