import { t } from '@lingui/core/macro';
import {
  ActionIcon,
  Alert,
  Anchor,
  Center,
  Divider,
  Drawer,
  Group,
  Loader,
  Paper,
  Stack,
  Text,
  Tooltip
} from '@mantine/core';
import {
  IconArrowRight,
  IconBellCheck,
  IconCircleCheck,
  IconExclamationCircle
} from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query';
import { useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';

import { Boundary } from '@lib/components/Boundary';
import { StylishText } from '@lib/components/StylishText';
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { ModelInformationDict } from '@lib/enums/ModelInformation';
import type { ModelType } from '@lib/enums/ModelType';
import { apiUrl } from '@lib/functions/Api';
import { getDetailUrl } from '@lib/functions/Navigation';
import { getBaseUrl } from '@lib/functions/Navigation';
import { navigateToLink } from '@lib/functions/Navigation';
import { api } from '../../App';
import { useUserState } from '../../states/UserState';

/**
 * Render a single notification entry in the drawer
 */
function NotificationEntry({
  notification,
  onRead
}: Readonly<{
  notification: any;
  onRead: () => void;
}>) {
  const navigate = useNavigate();

  let link = notification.target?.link;

  const model_type = notification.target?.model_type;
  const model_id = notification.target?.model_id;

  // If a valid model type is provided, that overrides the specified link
  if (model_type as ModelType) {
    const model_info = ModelInformationDict[model_type as ModelType];
    if (model_info?.url_detail && model_id) {
      link = getDetailUrl(model_type as ModelType, model_id);
    } else if (model_info?.url_overview) {
      link = model_info.url_overview;
    }
  }

  return (
    <Paper p='xs' shadow='xs'>
      <Group justify='space-between' wrap='nowrap'>
        <Tooltip
          label={notification.message}
          position='bottom-end'
          hidden={!notification.message}
        >
          <Stack gap={2}>
            <Anchor
              href={link ? `/${getBaseUrl()}${link}` : '#'}
              underline='hover'
              target='_blank'
              onClick={(event: any) => {
                if (link) {
                  // Mark the notification as read
                  onRead();
                }

                if (link.startsWith('/')) {
                  navigateToLink(link, navigate, event);
                }
              }}
            >
              <Text size='sm'>{notification.name}</Text>
            </Anchor>
            <Text size='xs'>{notification.age_human}</Text>
          </Stack>
        </Tooltip>
        <Tooltip label={t`Mark as read`} position='bottom-end'>
          <ActionIcon variant='transparent' onClick={onRead}>
            <IconBellCheck />
          </ActionIcon>
        </Tooltip>
      </Group>
    </Paper>
  );
}

/**
 * Construct a notification drawer.
 */
export function NotificationDrawer({
  opened,
  onClose
}: Readonly<{
  opened: boolean;
  onClose: () => void;
}>) {
  const { isLoggedIn } = useUserState();

  const navigate = useNavigate();

  const notificationQuery = useQuery({
    enabled: opened && isLoggedIn(),
    queryKey: ['notifications', opened],
    queryFn: async () =>
      api
        .get(apiUrl(ApiEndpoints.notifications_list), {
          params: {
            read: false,
            limit: 10,
            ordering: '-creation'
          }
        })
        .then((response) => response.data),
    refetchOnMount: false
  });

  const hasNotifications: boolean = useMemo(() => {
    return (notificationQuery.data?.results?.length ?? 0) > 0;
  }, [notificationQuery.data]);

  const markAllAsRead = useCallback(() => {
    api
      .post(apiUrl(ApiEndpoints.notifications_readall), {
        params: {
          read: false
        }
      })
      .catch((_error) => {})
      .then((_response) => {
        notificationQuery.refetch();
      });
  }, []);

  const markAsRead = useCallback((notification: any) => {
    api
      .patch(apiUrl(ApiEndpoints.notifications_list, notification.pk), {
        read: true
      })
      .then(() => {
        notificationQuery.refetch();
      })
      .catch(() => {
        notificationQuery.refetch();
      });
  }, []);

  return (
    <Drawer
      opened={opened}
      size='md'
      position='right'
      onClose={onClose}
      withCloseButton={false}
      styles={{
        header: {
          width: '100%'
        },
        title: {
          width: '100%'
        }
      }}
      title={
        <Group justify='space-between' wrap='nowrap'>
          <StylishText size='lg'>{t`Notifications`}</StylishText>
          <Group justify='end' wrap='nowrap'>
            <Tooltip label={t`Mark all as read`}>
              <ActionIcon
                variant='transparent'
                onClick={() => {
                  markAllAsRead();
                }}
              >
                <IconBellCheck />
              </ActionIcon>
            </Tooltip>
            <Tooltip label={t`View all notifications`}>
              <ActionIcon
                onClick={(event: any) => {
                  onClose();
                  navigateToLink('/notifications/unread', navigate, event);
                }}
                variant='transparent'
              >
                <IconArrowRight />
              </ActionIcon>
            </Tooltip>
          </Group>
        </Group>
      }
    >
      <Boundary label='NotificationDrawer'>
        <Stack gap='xs'>
          <Divider />
          {!notificationQuery.isFetching &&
            !notificationQuery.isLoading &&
            !notificationQuery.isError &&
            !hasNotifications && (
              <Alert color='green' icon={<IconCircleCheck />}>
                <Text size='sm'>{t`You have no unread notifications.`}</Text>
              </Alert>
            )}
          {hasNotifications &&
            notificationQuery.data?.results?.map((notification: any) => (
              <NotificationEntry
                key={`notification-${notification.pk}`}
                notification={notification}
                onRead={() => markAsRead(notification)}
              />
            ))}
          {notificationQuery.isFetching && (
            <Center>
              <Loader size='sm' />
            </Center>
          )}
          {notificationQuery.isError && (
            <Alert
              color='red'
              title={t`Error`}
              icon={<IconExclamationCircle />}
            >
              <Text size='sm'>{t`Error loading notifications.`}</Text>
            </Alert>
          )}
        </Stack>
      </Boundary>
    </Drawer>
  );
}
