import {
  useCallback,
  useEffect,
  useLayoutEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import {ScrollView, StyleSheet, View} from 'react-native';
import {useTranslation} from 'react-i18next';
import {useMutation, useQueryClient} from 'react-query';
import {
  IconArrowBackUp,
  IconBan,
  IconCheck,
  IconChevronRight,
  IconInfoCircle,
  IconPencil,
  IconX,
} from '@tabler/icons-react-native';
import {
  Approvers,
  BottomModalFooter,
  CardContainer,
  IconButton,
  InputTextArea,
  ListRow,
  Pill,
  ProgressBar,
  Title,
  ApprovalSteps,
  Typography,
  ButtonVariant,
  RefreshControl,
  Divider,
  Alert,
} from '@components';
import {EventsSocketsType} from '@config/constants';
import {SocketConnection} from '@config/socket/socket';
import {useSafeAreaBottomPadding} from '@hooks/useSafeAreaBottomPadding';
import {useGoBack} from '@hooks/useGoBack';
import {useModalHandler} from '@hooks/useModalHandler';
import {Navigation} from '@interfaces/navigation';
import {TIME_TRACKING_QUERY_KEYS} from '@modules/timeTracking/constants';
import {
  updateOvertimeRequest,
  updateOvertimeStepRequest,
} from '@modules/timeTracking/services';
import TimeSlotsDisplayer from '@modules/timeTracking/components/TimeSlotsDisplayer';
import DoorDisplayer from '@modules/timeTracking/components/DoorDisplayer';
import {transformTimeFromHours} from '@modules/timeTracking/utils';
import {
  CategorizedHourStatusUpdated,
  OvertimeRequestEntry,
  OvertimeRequestStatus,
  TrackerEventType,
} from '@modules/timeTracking/interfaces';
import {TimeOffStepState} from '@modules/timeOff/interfaces';
import {
  useGetOvertimeRequest,
  useTimeTrackingGrants,
} from '@modules/timeTracking/hooks';
import {showSnackbar} from '@redux/dispatchers';
import {useTheme} from '@shared/theme';
import {Screens} from '@shared/constants';
import {getCompactDate, getCompleteName} from '@shared/utils';

import CancelledDialog from './components/CancelledDialog';
import RequestDetailSk from './components/RequestDetailSk';
import RevokeRequestDialog from './components/RevokeRequestDialog';
import RowItem from './components/RowItem';
import {styles} from './styles';
import {getStatusPill} from './utils';

const socket = new SocketConnection();

function OvertimeRequestDetail({
  navigation,
  route: {
    params: {requestId},
  },
}: Navigation<Screens.OVERTIME_REQUEST_DETAIL>) {
  const {t} = useTranslation();
  const queryClient = useQueryClient();
  const {goBack} = useGoBack();
  const scrollViewRef = useRef<ScrollView>(null);
  const {theme, iconSizes, spacing} = useTheme();
  const [note, setNote] = useState('');
  const [revokeReason, setRevokeReason] = useState('');
  const [isEditing, setIsEditing] = useState(false);
  const [showUpdateAlert, setShowUpdateAlert] = useState(false);
  const {
    isVisible: isCancelledDialogVisible,
    onOpenModal: onOpenCancelledDialog,
    onCloseModal: onCloseCancelledDialog,
  } = useModalHandler();
  const {
    isVisible: isRevokeDialogVisible,
    onOpenModal: openRevokeDialog,
    onCloseModal: closeRevokeDialog,
  } = useModalHandler();
  const paddingBottom = useSafeAreaBottomPadding({bottom: spacing.x2});
  const {manageTimeTracking: iAmAdmin} = useTimeTrackingGrants();

  useLayoutEffect(() => {
    navigation.setOptions({title: t('general.detail')});
  }, [navigation, t]);

  const {
    approvalStepsCount,
    approvers,
    approversByStep,
    approversCount,
    currentStepId,
    formattedReasonNotes,
    isLoadingRequest,
    iAmPreviousStepApprover,
    isRefreshingRequest,
    onRefreshRequest,
    previousStepId,
    request,
    requestProgress,
  } = useGetOvertimeRequest(requestId);

  const isPending = request?.status === OvertimeRequestStatus.PENDING;
  const isRejected = request?.status === OvertimeRequestStatus.REJECTED;
  const isApproved = request?.status === OvertimeRequestStatus.APPROVED;
  const isCancelled =
    request?.status === OvertimeRequestStatus.CANCELLED_BY_MODIFICATION;
  const isRevoked = isRejected && request?.isStatusOverridden;
  const iAmApprover = iAmAdmin || !!request?.isLoggedUserPotentialApprover;
  const canActionApproval =
    (iAmApprover && isPending) || (iAmPreviousStepApprover && isEditing);
  const canRevokeApproval = iAmAdmin && isApproved;
  const canRevertRevokeApproval = iAmAdmin && isRevoked && isEditing;
  const currentPaddingBottom =
    canActionApproval || canRevokeApproval || canRevertRevokeApproval
      ? 0
      : paddingBottom;
  const {key: statusKey, variant: statusVariant} = getStatusPill(request);
  const itemContainerStyle = StyleSheet.flatten([
    styles.itemContainer,
    {backgroundColor: theme.background.elements.grey},
  ]);

  const approvalStepId = useMemo(
    () =>
      isEditing
        ? request?.currentStepId
          ? previousStepId
          : currentStepId
        : currentStepId,
    [currentStepId, isEditing, previousStepId, request?.currentStepId],
  );

  const {mutate: revertRevokeApproval, isLoading: isApprovingRequest} =
    useMutation(updateOvertimeRequest, {
      onSuccess: () => {
        goBack();
        queryClient.invalidateQueries({
          queryKey: TIME_TRACKING_QUERY_KEYS.historyOvertimeRequestsAll,
        });
        showSnackbar({
          title: t('time_tracker.overtime_request_detail.approve_success'),
          variant: 'success',
        });
      },
      onError: () => {
        showSnackbar({
          description: t(
            'time_tracker.overtime_request_detail.change_error_desc',
          ),
          title: t('time_tracker.overtime_request_detail.approve_error'),
          variant: 'error',
        });
      },
    });

  const onRevertRevokeApproval = useCallback(() => {
    revertRevokeApproval({
      id: requestId,
      status: OvertimeRequestStatus.APPROVED,
      comment: note.trim() || undefined,
    });
  }, [note, requestId, revertRevokeApproval]);

  const onResetOvertimeRequestsInfo = useCallback(() => {
    queryClient.invalidateQueries({
      queryKey: TIME_TRACKING_QUERY_KEYS.pendingOvertimeRequestsAmount,
    });
    queryClient.invalidateQueries({
      queryKey: TIME_TRACKING_QUERY_KEYS.pendingOvertimeRequestsAll,
    });
    queryClient.invalidateQueries({
      queryKey: TIME_TRACKING_QUERY_KEYS.historyOvertimeRequestsAll,
    });
  }, [queryClient]);

  const {mutate: updateRequestStepStatus, isLoading: isUpdatingRequestStatus} =
    useMutation(updateOvertimeStepRequest, {
      onSuccess: () => {
        goBack();
        onResetOvertimeRequestsInfo();
        showSnackbar({
          title: t(
            `time_tracker.${
              isEditing
                ? 'response_modified_successfully'
                : 'response_completed_successfully'
            }`,
          ),
          variant: 'success',
        });
      },
      onError: () => {
        showSnackbar({
          description: t('time_tracker.request_response_error_description'),
          title: t('time_tracker.request_response_error'),
          variant: 'error',
        });
      },
    });

  const onPressStatusChange = useCallback(
    (state: TimeOffStepState) => () => {
      if (!approvalStepId) return;
      updateRequestStepStatus({
        id: requestId,
        approvalStepId,
        state,
        comment: note.trim() || undefined,
      });
    },
    [approvalStepId, note, requestId, updateRequestStepStatus],
  );

  const onPressEdit = useCallback(() => {
    setIsEditing(true);
    // Wait for the state update to complete before scrolling to the end
    requestAnimationFrame(() => {
      scrollViewRef.current?.scrollToEnd({animated: true});
    });
  }, []);

  const onCancelEdit = useCallback(() => {
    setIsEditing(false);
    setNote('');
    scrollViewRef.current?.scrollTo({y: 0, animated: true});
  }, []);

  const onOpenRevokeDialog = useCallback(() => {
    setRevokeReason('');
    openRevokeDialog();
  }, [openRevokeDialog]);

  const onCloseRevokeDialog = useCallback(() => {
    setRevokeReason('');
    closeRevokeDialog();
  }, [closeRevokeDialog]);

  const revokeApprovalFooter = useMemo(
    () => ({
      primaryButton: {
        onPress: onOpenRevokeDialog,
        IconLeft: IconBan,
        text: t('time_tracker.overtime_request_detail.revoke'),
        variant: 'error' as ButtonVariant,
      },
    }),
    [onOpenRevokeDialog, t],
  );

  const revertRevokeApprovalFooter = useMemo(
    () => ({
      primaryButton: {
        isLoading: isApprovingRequest,
        onPress: onRevertRevokeApproval,
        text: t('time_tracker.overtime_request_detail.change_to_approved'),
      },
      secondaryButton: {
        disabled: isApprovingRequest,
        onPress: onCancelEdit,
        text: t('time_tracker.exit_without_saving'),
      },
    }),
    [isApprovingRequest, onCancelEdit, onRevertRevokeApproval, t],
  );

  const editStepApprovalFooter = useMemo(
    () => ({
      primaryButton: isRejected
        ? {
            IconLeft: IconCheck,
            isLoading: isUpdatingRequestStatus,
            onPress: onPressStatusChange(TimeOffStepState.APPROVED),
            text: t('time_tracker.overtime_request_detail.change_to_approved'),
          }
        : {
            IconLeft: IconX,
            isLoading: isUpdatingRequestStatus,
            onPress: onPressStatusChange(TimeOffStepState.REJECTED),
            text: t('time_tracker.overtime_request_detail.change_to_rejected'),
          },
      secondaryButton: {
        disabled: isUpdatingRequestStatus,
        onPress: onCancelEdit,
        text: t('time_tracker.exit_without_saving'),
      },
    }),
    [isRejected, isUpdatingRequestStatus, onCancelEdit, onPressStatusChange, t],
  );

  const pendingStepApprovalFooter = useMemo(
    () => ({
      primaryButton: {
        IconLeft: IconCheck,
        isLoading: isUpdatingRequestStatus,
        onPress: onPressStatusChange(TimeOffStepState.APPROVED),
        text: t('general.approve'),
      },
      secondaryButton: {
        disabled: isUpdatingRequestStatus,
        IconLeft: IconX,
        onPress: onPressStatusChange(TimeOffStepState.REJECTED),
        text: t('general.reject'),
      },
    }),
    [isUpdatingRequestStatus, onPressStatusChange, t],
  );

  const renderEntry = useCallback(
    (entry: OvertimeRequestEntry) => (
      <CardContainer key={entry.id} style={styles.entryContainer}>
        <DoorDisplayer
          dateColor={theme.text.neutral.default}
          isStart={entry.type === TrackerEventType.START}
          time={entry.time}
        />
      </CardContainer>
    ),
    [theme.text.neutral.default],
  );

  useEffect(() => {
    const onStatusUpdated = (data: CategorizedHourStatusUpdated) => {
      if (data.categorizedHourId !== requestId) return;
      onRefreshRequest();
      onCloseRevokeDialog();
      onCloseCancelledDialog();
      onCancelEdit();
      setShowUpdateAlert(true);
      onResetOvertimeRequestsInfo();
    };

    socket.listenEvent(
      EventsSocketsType.CATEGORIZED_HOUR_STATUS_UPDATED,
      onStatusUpdated,
    );

    return () => {
      socket.unListenEvent(
        EventsSocketsType.CATEGORIZED_HOUR_STATUS_UPDATED,
        onStatusUpdated,
      );
    };
  }, [
    onCancelEdit,
    onCloseCancelledDialog,
    onCloseRevokeDialog,
    onRefreshRequest,
    onResetOvertimeRequestsInfo,
    queryClient,
    requestId,
  ]);

  return (
    <View
      style={[
        styles.flex,
        {
          backgroundColor: theme.background.layout.tertiary,
          paddingBottom: currentPaddingBottom,
        },
      ]}>
      {!request || isLoadingRequest ? (
        <RequestDetailSk />
      ) : (
        <View style={styles.flex}>
          <ScrollView
            contentContainerStyle={styles.scrollView}
            ref={scrollViewRef}
            refreshControl={
              <RefreshControl
                refreshing={isRefreshingRequest}
                onRefresh={onRefreshRequest}
              />
            }
            showsVerticalScrollIndicator={false}
            style={styles.flex}>
            {approvalStepsCount > 1 &&
              (approvalStepsCount > 3 ? (
                <ProgressBar
                  title={t(
                    'time_tracker.overtime_request_detail.approval_steps',
                  )}
                  rightSideText={t(
                    `time_tracker.overtime_request_detail.status.${
                      isRejected
                        ? 'rejected'
                        : isApproved
                        ? 'approved'
                        : 'in_process'
                    }`,
                  )}
                  total={approvalStepsCount}
                  progress={requestProgress}
                  helper={`${requestProgress}/${approvalStepsCount}`}
                />
              ) : (
                <ApprovalSteps
                  approvalSteps={request.approvalSteps}
                  differentLines
                />
              ))}
            <ListRow
              disabled
              backgroundColor={theme.background.elements.grey}
              style={styles.itemContainer}>
              <ListRow.Avatar
                name={request.fullName}
                size="sm"
                url={request.profilePicture}
              />
              <ListRow.Title
                title={request.fullName}
                topText={t('time_tracker.overtime_request_detail.requested_by')}
              />
            </ListRow>
            <Title
              size="s"
              style={itemContainerStyle}
              title={getCompactDate(request.dateString)}
              topText={t('time_tracker.overtime_request_detail.request_date')}
            />
            <RowItem
              style={itemContainerStyle}
              topText={t('time_tracker.overtime_request_detail.request_type')}>
              <Pill text={request.hourCategoryName} variant="info" />
            </RowItem>
            {!!request.workTimeSlots.length && (
              <RowItem
                style={itemContainerStyle}
                topText={t(
                  'time_tracker.overtime_request_detail.work_schedule',
                )}>
                <TimeSlotsDisplayer timeSlots={request.workTimeSlots} />
              </RowItem>
            )}
            <RowItem
              style={itemContainerStyle}
              topText={t(
                'time_tracker.overtime_request_detail.response_responsible',
              )}
              rightChildren={
                <IconChevronRight
                  color={theme.text.neutral.default}
                  size={iconSizes.x4}
                  style={styles.rightIcon}
                />
              }>
              {!!approversByStep && !!approversCount && (
                <Approvers
                  approvers={approvers}
                  approversCount={approversCount}
                  steps={request.approvalSteps}
                  approversByStep={approversByStep}
                  isLoadingApprovers={false}
                  isPending={request.isLoggedUserPotentialApprover}
                  shouldOpenModal
                  emptyApproversText={t(
                    `time_tracker.overtime_request_detail.approval_step_empty_approvers.${
                      iAmAdmin ? 'admin' : 'other'
                    }`,
                  )}
                />
              )}
            </RowItem>
            <RowItem
              style={[itemContainerStyle, styles.entriesContainer]}
              topText={t('time_tracker.overtime_request_detail.request_status')}
              rightChildren={
                (isCancelled ||
                  (iAmAdmin && isRevoked) ||
                  (iAmPreviousStepApprover && !isRevoked)) && (
                  <IconButton
                    disabled={isUpdatingRequestStatus}
                    Icon={
                      isCancelled
                        ? IconInfoCircle
                        : isEditing
                        ? IconArrowBackUp
                        : IconPencil
                    }
                    onPress={
                      isCancelled
                        ? onOpenCancelledDialog
                        : isEditing
                        ? onCancelEdit
                        : onPressEdit
                    }
                    variant="secondary"
                  />
                )
              }>
              <View style={styles.pillsRow}>
                {iAmPreviousStepApprover && (isPending || isApproved) && (
                  <Pill
                    text={t('time_tracker.approved_by_me')}
                    variant="success"
                  />
                )}
                {request.status && (
                  <Pill text={t(statusKey)} variant={statusVariant} />
                )}
              </View>
              {showUpdateAlert && (
                <Alert
                  description={t(
                    `approval_requests.step_already_reviewed_description_${
                      request?.isLoggedUserPotentialApprover
                        ? 'pending'
                        : 'in_process'
                    }`,
                  )}
                  title={t('approval_requests.step_already_reviewed_title', {
                    step: Math.max(
                      1,
                      requestProgress - (request?.currentStepId ? 1 : 0),
                    ),
                  })}
                  style={styles.alertContainer}
                  variant="info"
                />
              )}
            </RowItem>
            {(request.isStatusOverridden || !!formattedReasonNotes) && (
              <RowItem
                style={itemContainerStyle}
                topText={t('approval_requests.response_reason')}>
                {request.isStatusOverridden && (
                  <Typography>
                    <Typography variant="xs" weight="semiBold">
                      {t(
                        isRejected
                          ? 'approval_requests.revoke_comment_intro'
                          : 'approval_requests.change_to_approved_comment_intro',
                        {name: getCompleteName(request.statusOverride?.admin)},
                      )}
                    </Typography>
                    {!!request.statusOverride?.comment && (
                      <Typography variant="xs">
                        {`: ${request.statusOverride?.comment}`}
                      </Typography>
                    )}
                  </Typography>
                )}
                {request.isStatusOverridden && !!formattedReasonNotes && (
                  <Divider style={styles.divider} />
                )}
                {!!formattedReasonNotes && (
                  <Typography variant="xs">{formattedReasonNotes}</Typography>
                )}
              </RowItem>
            )}
            <Title
              size="s"
              style={itemContainerStyle}
              title={transformTimeFromHours(request.hours)}
              topText={t(
                'time_tracker.overtime_request_detail.hours_to_approve',
              )}
            />
            <RowItem
              style={itemContainerStyle}
              topText={t('time_tracker.overtime_request_detail.markings')}>
              <ScrollView
                horizontal
                bounces={false}
                contentContainerStyle={styles.entriesContainer}
                showsHorizontalScrollIndicator={false}>
                {request.entries.map(renderEntry)}
              </ScrollView>
            </RowItem>
            {(canActionApproval || canRevertRevokeApproval) && (
              <View style={itemContainerStyle}>
                <InputTextArea
                  multiline
                  value={note}
                  label={t('approval_requests.response_reason')}
                  onChangeText={setNote}
                  placeholder={t(
                    'time_tracker.overtime_request_detail.write_comments_placeholder',
                  )}
                  textAlignVertical="top"
                />
              </View>
            )}
          </ScrollView>
          {(canActionApproval ||
            canRevokeApproval ||
            canRevertRevokeApproval) && (
            <BottomModalFooter
              footer={
                canRevertRevokeApproval
                  ? revertRevokeApprovalFooter
                  : canRevokeApproval
                  ? isEditing
                    ? editStepApprovalFooter
                    : revokeApprovalFooter
                  : isEditing
                  ? editStepApprovalFooter
                  : pendingStepApprovalFooter
              }
            />
          )}
          <CancelledDialog
            isVisible={isCancelledDialogVisible}
            onClose={onCloseCancelledDialog}
          />
          <RevokeRequestDialog
            isVisible={isRevokeDialogVisible}
            onChangeRevokeReason={setRevokeReason}
            onClose={onCloseRevokeDialog}
            requestId={requestId}
            revokeReason={revokeReason}
          />
        </View>
      )}
    </View>
  );
}

export default OvertimeRequestDetail;
