import { useForm } from 'react-hook-form';
import { useMutation, useQuery, useQueryClient } from 'react-query';
import { useNavigate } from 'react-router';

import { type AxiosResponse } from 'axios';
import { parseAsInteger, useQueryState } from 'nuqs';
import { useDebounce } from '@material-hu/hooks/useDebounce';
import { useDrawer } from '@material-hu/hooks/useDrawer';
import { useModal } from '@material-hu/hooks/useModal';
import {
  IconCopy,
  IconEdit,
  IconInfoCircle,
  IconToggleLeft,
  IconToggleRight,
  IconTrash,
} from '@material-hu/icons/tabler';

import Button from '@material-hu/components/design-system/Buttons/Button';
import HuDialog from '@material-hu/components/design-system/Dialog';
import useHuSnackbar from '@material-hu/components/design-system/Snackbar';

import {
  activateWorkflow,
  createWorkflow,
  deactivateWorkflow,
  deleteWorkflow,
  duplicateWorkflow,
  getWorkflows,
} from 'src/services/workflowsService';
import {
  type WorkflowDetail,
  WorkflowStatus,
  WorkflowsScreenSource,
} from 'src/types/workflows';
import { insertIf } from 'src/utils/arrayUtils';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { LogEvents, logEvent } from 'src/utils/logging';
import { type PaginatedResponse } from 'src/utils/tableUtils';

import useModuleConfiguration from '../../ServiceManagement/hooks/useModuleConfiguration';
import useServiceMgmtPermissions from '../../ServiceManagement/hooks/useServiceMgmtPermissions';
import Detail from '../components/Detail';
import New from '../components/New';
import { workflowsKeys } from '../queries';
import { workflowsRoutes } from '../routes';
import { updateWorkflowStatus } from '../utils';

const WORKFLOW_NAME_MAX_LENGTH = 255;
const DEFAULT_LIMIT = 10;
const LIMIT_OPTIONS = [10, 20, 30];

const useWorkflowsTable = () => {
  const navigate = useNavigate();
  const { t } = useLokaliseTranslation([
    'workflows',
    'general',
    'service_management',
  ]);
  const queryClient = useQueryClient();
  const { enqueueSnackbar } = useHuSnackbar();

  const { canCreateWorkflows, canEditWorkflows, canDeleteWorkflows } =
    useServiceMgmtPermissions();

  const { moduleActivated, isLoadingModuleConfiguration } =
    useModuleConfiguration();

  const form = useForm({
    defaultValues: {
      title: '',
      description: '',
    },
    mode: 'onChange',
  });
  const { title, description } = form.watch();

  const [query, setQuery] = useQueryState('search', { defaultValue: '' });
  const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1));
  const [limit, setLimit] = useQueryState(
    'limit',
    parseAsInteger.withDefault(DEFAULT_LIMIT),
  );

  const debouncedSearch = useDebounce(query);

  const handleQueryChange = (value: string) => {
    setQuery(value);
    setPage(1);
  };

  const handleLimitChange = (newLimit: number) => {
    setLimit(newLimit);
    setPage(1);
  };

  const pagination = { page, limit };
  const formatFilter = { page: page - 1, limit, search: debouncedSearch };

  const { data, isLoading, isFetching } = useQuery(
    workflowsKeys.workflows(formatFilter),
    () => getWorkflows(formatFilter),
    {
      select: response => response.data,
      keepPreviousData: true,
      enabled: moduleActivated,
    },
  );

  const workflows = data?.items || [];
  const totalPages = data?.totalPages || 0;
  // `isLoading` from react-query is true only on the very first fetch (no
  // cached data yet) when `keepPreviousData` is enabled. Subsequent refetches
  // keep the previous data and report `isFetching` instead.
  const showSkeleton = isLoading;
  const isRefetching = !showSkeleton && isFetching;
  const hasItems = workflows.length > 0;
  const showWorkflows = !showSkeleton && hasItems;
  const showEmptyState = !showSkeleton && !hasItems && !debouncedSearch;
  const showNoResults = !showSkeleton && !hasItems && !!debouncedSearch;
  const showPagination = showSkeleton || hasItems;
  const showActivateModuleCard =
    !isLoadingModuleConfiguration && !moduleActivated;

  const createWorkflowMutation = useMutation(
    () => createWorkflow(title, description),
    {
      onSuccess: ({ data: { id } }) => {
        navigate(workflowsRoutes.workflow(id));
        logEvent(LogEvents.WORKFLOW_CREATED, { workflowId: id });
      },
      onError: (error: { response: { data: { code: string } } }) => {
        if (error.response.data.code === 'DUPLICATE_ENTITY') {
          form.setError('title', { message: t('duplicated_workflow_name') });
        } else {
          enqueueSnackbar({
            title: t('workflow_creation_error'),
            variant: 'error',
          });
        }
      },
    },
  );

  const {
    showDrawer: showNewDrawer,
    drawer: newDrawer,
    closeDrawer,
  } = useDrawer(New, {
    title: t('new_workflow_title'),
    primaryButtonProps: {
      children: t('general:create'),
      onClick: () => createWorkflowMutation.mutate(),
      fullWidth: true,
      disabled: !form.formState.isValid || createWorkflowMutation.isLoading,
      loading: createWorkflowMutation.isLoading,
    },
    secondaryButtonProps: {
      children: t('general:cancel'),
      onClick: () => {
        closeDrawer();
        form.reset();
      },
      fullWidth: true,
    },
    onClose: () => {
      closeDrawer();
      form.reset();
    },
  });

  const {
    showDrawer: showDetailDrawer,
    drawer: detailDrawer,
    closeDrawer: closeDetailDrawer,
  } = useDrawer(Detail, {
    title: t('workflows_details'),
    footer: (
      <Button
        variant="primary"
        onClick={() => closeDetailDrawer()}
      >
        {t('general:close')}
      </Button>
    ),
    onClose: () => {
      closeDetailDrawer();
    },
  });

  const toggleWorkflowStatus = useMutation(
    ({ id, isActive }: { id: string; isActive: boolean }) =>
      !isActive ? activateWorkflow(id) : deactivateWorkflow(id),
    {
      onSuccess: (_, { id, isActive }) => {
        queryClient.setQueryData<
          AxiosResponse<PaginatedResponse<WorkflowDetail>> | undefined
        >(workflowsKeys.workflows(formatFilter), old =>
          updateWorkflowStatus(old, id, isActive),
        );
        enqueueSnackbar({
          title: !isActive
            ? t('workflow_activated')
            : t('workflow_deactivated'),
          variant: 'success',
        });
        logEvent(LogEvents.WORKFLOW_STATUS_CHANGED, {
          workflowId: id,
          workflowStatus: isActive
            ? WorkflowStatus.INACTIVE
            : WorkflowStatus.ACTIVE,
          screen_source: WorkflowsScreenSource.WORKFLOW_LIST,
        });
      },
      onError: () => {
        enqueueSnackbar({
          title: t('workflow_activation_error'),
          variant: 'error',
        });
      },
    },
  );

  const handleWorkflowStatus = (workflow: WorkflowDetail) => {
    toggleWorkflowStatus.mutate({
      id: workflow.id,
      isActive: workflow.status === WorkflowStatus.ACTIVE,
    });
  };

  const deleteWorkflowMutation = useMutation(
    ({ id }: { id: string }) => deleteWorkflow(id),
    {
      onSuccess: (_, { id }) => {
        queryClient.invalidateQueries(workflowsKeys.workflows(formatFilter));
        enqueueSnackbar({
          title: t('workflow_deleted'),
          variant: 'success',
        });
        logEvent(LogEvents.WORKFLOW_DELETED, { workflowId: id });
      },
      onError: () => {
        enqueueSnackbar({
          title: t('workflow_deletion_error'),
          variant: 'error',
        });
      },
    },
  );

  const duplicateWorkflowMutation = useMutation(
    ({ id, newName }: { id: string; newName: string }) =>
      duplicateWorkflow(id, newName),
    {
      onSuccess: ({ data: { id } }) => {
        navigate(workflowsRoutes.workflow(id), {
          state: { fromDuplicate: true },
        });
      },
      onError: (error: { response?: { data?: { code?: string } } }) => {
        if (error.response?.data?.code === 'DUPLICATE_ENTITY') {
          enqueueSnackbar({
            title: t('workflow_duplicate_name_in_use.title'),
            description: t('workflow_duplicate_name_in_use.description'),
            variant: 'error',
          });
          return;
        }
        enqueueSnackbar({
          title: t('workflow_duplicate_error'),
          variant: 'error',
        });
      },
    },
  );

  const handleDuplicate = (workflow: WorkflowDetail) => {
    let newName = `${t('service_management:copy_of')} ${workflow.name}`;
    if (newName.length > WORKFLOW_NAME_MAX_LENGTH) {
      newName = newName.substring(0, WORKFLOW_NAME_MAX_LENGTH);
    }
    duplicateWorkflowMutation.mutate({ id: workflow.id, newName });
  };

  const {
    modal: deleteModal,
    showModal: showDeleteModal,
    closeModal: closeDeleteModal,
  } = useModal<{ id: string }>(({ id }) => (
    <HuDialog
      title={t('delete_workflow_sure')}
      textBody={t('delete_workflow_body')}
      primaryButtonProps={{
        loading: deleteWorkflowMutation.isLoading,
        children: t('general:delete'),
        onClick: () =>
          deleteWorkflowMutation.mutateAsync(
            { id },
            { onSettled: closeDeleteModal },
          ),
      }}
      secondaryButtonProps={{
        children: t('general:cancel'),
        onClick: closeDeleteModal,
      }}
      onClose={closeDeleteModal}
    />
  ));

  const getMenuOptions = (workflow: WorkflowDetail) => [
    {
      title: t('details'),
      Icon: IconInfoCircle,
      onClick: () => {
        showDetailDrawer({ workflow });
        logEvent(LogEvents.WORKFLOW_DETAILS_VIEWED, {
          workflowId: workflow.id,
        });
      },
    },
    ...insertIf(canEditWorkflows, {
      title: t('edit'),
      Icon: IconEdit,
      onClick: () => navigate(workflowsRoutes.workflow(workflow.id)),
    }),
    ...insertIf(canCreateWorkflows || canEditWorkflows, {
      title: t('general:duplicate'),
      Icon: IconCopy,
      onClick: () => handleDuplicate(workflow),
    }),
    ...insertIf(workflow.status !== WorkflowStatus.DRAFT && canEditWorkflows, {
      title: t('action', { context: workflow.status }),
      Icon:
        workflow.status === WorkflowStatus.ACTIVE
          ? IconToggleLeft
          : IconToggleRight,
      onClick: () => handleWorkflowStatus(workflow),
    }),
    ...insertIf(canDeleteWorkflows, {
      title: t('general:delete'),
      Icon: IconTrash,
      onClick: () => showDeleteModal({ id: workflow.id }),
    }),
  ];

  const handleWorkflowClick = (workflow: WorkflowDetail) => {
    navigate(workflowsRoutes.workflow(workflow.id));
    logEvent(LogEvents.WORKFLOW_BUILDER_OPENED, {
      workflowId: workflow.id,
    });
  };

  return {
    form,
    query,
    setQuery: handleQueryChange,
    pagination,
    setPage,
    setLimit: handleLimitChange,
    limitOptions: LIMIT_OPTIONS,
    workflows,
    totalPages,
    showSkeleton,
    isRefetching,
    showEmptyState,
    showNoResults,
    showWorkflows,
    showPagination,
    canCreateWorkflows,
    isLoadingModuleConfiguration,
    showActivateModuleCard,
    newDrawer,
    detailDrawer,
    deleteModal,
    showNewDrawer,
    getMenuOptions,
    handleWorkflowClick,
  };
};

export default useWorkflowsTable;
