import LoaderIcon from '@/components/icons/loader-icon';
import fetcher, { request } from '@/lib/fetcher';
import { cn } from '@/lib/utils';
import { Notification, SharedData } from '@/types';
import { router, usePage } from '@inertiajs/react';
import { useEcho } from '@laravel/echo-react';
import {
  BellSimpleIcon,
  CheckCircleIcon,
  EnvelopeSimpleIcon,
  InfoIcon,
  WarningIcon,
  XCircleIcon,
  XIcon,
} from '@phosphor-icons/react';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { toast } from 'sonner';
import useSWRInfinite from 'swr/infinite';
import { Button } from '../ui/button';
import { Separator } from '../ui/separator';
import {
  Sheet,
  SheetClose,
  SheetContent,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from '../ui/sheet';
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from '../ui/tooltip';

dayjs.extend(relativeTime);

const PER_PAGE = 15;

const notificationTypeConfig = {
  info: {
    Icon: InfoIcon,
    container: 'border-[#3BA0FF]/20 bg-[#3BA0FF]/10',
    icon: 'text-info',
  },
  success: {
    Icon: CheckCircleIcon,
    container: 'border-success/20 bg-[#95FF00]/10',
    icon: 'text-success',
  },
  warning: {
    Icon: WarningIcon,
    container: 'border-warning/20 bg-[#FFC424]/10',
    icon: 'text-warning',
  },
  error: {
    Icon: XCircleIcon,
    container: 'border-destructive/20 bg-[#DD2934]/10',
    icon: 'text-destructive',
  },
} as const;

export default function UserNotification({
  admin = false,
}: {
  admin?: boolean;
}) {
  const [isOpen, setIsOpen] = useState(false);
  const { t } = useTranslation();
  const { auth } = usePage<SharedData>().props;
  const sentinelRef = useRef<HTMLDivElement>(null);
  const scrollContainerRef = useRef<HTMLDivElement>(null);

  const getKey = (
    pageIndex: number,
    previousPageData: { next_page_url: string | null } | null,
  ) => {
    if (previousPageData && !previousPageData.next_page_url) return null;
    return `/api/user/notifications?page=${pageIndex + 1}&perPage=${PER_PAGE}`;
  };

  const { data, mutate, size, setSize, isLoading, isValidating } =
    useSWRInfinite(getKey, fetcher, {
      revalidateOnFocus: false,
      revalidateFirstPage: false,
    });

  const notifications: Notification[] = data
    ? data.flatMap((page) => page?.data ?? [])
    : [];
  const hasMore = data ? !!data[data.length - 1]?.next_page_url : false;
  const unreadCount = notifications.filter((n) => !n.is_read).length;
  const isLoadingMore = isValidating && size > 1;

  useEcho(`notifications.${auth.user.id}`, 'NewNotification', (e) => {
    const toastFn =
      (toast[e.type as 'success' | 'error' | 'warning' | 'info'] as (
        msg: string,
        opts?: object,
      ) => void) ?? toast;
    toastFn(e.title, { description: e.message });
    mutate();
  });

  useEffect(() => {
    const sentinel = sentinelRef.current;
    if (!sentinel) return;

    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting && hasMore && !isValidating) {
          setSize((s) => s + 1);
        }
      },
      { threshold: 0.1 },
    );

    observer.observe(sentinel);
    return () => observer.disconnect();
  }, [hasMore, isValidating, setSize]);

  useEffect(() => {
    if (!isOpen) setSize(1);
  }, [isOpen, setSize]);

  const [isMarkingAllRead, setIsMarkingAllRead] = useState(false);

  const handleMarkAllAsRead = useCallback(async () => {
    setIsMarkingAllRead(true);
    try {
      await request.post('/api/user/notifications/mark-all-as-read');
      mutate();
    } catch {
      toast.error(t('Failed to mark notifications as read'));
    } finally {
      setIsMarkingAllRead(false);
    }
  }, [mutate, t]);

  const handleNotificationClick = useCallback(
    async (notification: Notification) => {
      if (!notification.is_read) {
        try {
          await request.post(
            `/api/user/notifications/${notification.id}/mark-as-read`,
          );
          mutate();
        } catch {
          // silent
        }
      }
      if (notification.link) router.visit(notification.link);
    },
    [mutate],
  );

  return (
    <Sheet open={isOpen} onOpenChange={setIsOpen}>
      <TooltipProvider delayDuration={0}>
        <Tooltip>
          <SheetTrigger asChild>
            <TooltipTrigger asChild>
              {admin ? (
                <button
                  type="button"
                  className="relative flex h-12 w-12 cursor-pointer items-center justify-center rounded-full bg-white/50 backdrop-blur-xs transition-[background] duration-300 ease-out hover:bg-white/70 dark:bg-black/30 dark:hover:bg-black/60"
                >
                  <BellSimpleIcon
                    weight={admin ? 'bold' : 'fill'}
                    className="size-6 opacity-80 group-hover:opacity-100"
                  />
                  {unreadCount > 0 && (
                    <span className="absolute top-1.5 right-1.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] leading-none font-bold text-white">
                      {unreadCount > 99 ? '99+' : unreadCount}
                    </span>
                  )}
                </button>
              ) : (
                <Button variant="icon">
                  <BellSimpleIcon
                    weight="bold"
                    className="size-6 opacity-80 group-hover:opacity-100"
                  />
                </Button>
              )}
            </TooltipTrigger>
          </SheetTrigger>

          <TooltipContent>
            <p>{t('Notifications')}</p>
          </TooltipContent>
        </Tooltip>
      </TooltipProvider>

      <SheetContent className="border-0 border-l border-black/8 bg-background shadow-xl dark:border-white/5">
        <div className="relative z-10 flex h-full flex-col">
          <SheetHeader isAdmin={false} className="justify-between px-6 pb-4">
            <div className="flex items-center gap-2">
              <SheetClose className="flex size-10 cursor-pointer items-center justify-center rounded-xl border-0 bg-black/8 hover:bg-black/12 dark:bg-white/10 dark:hover:bg-white/15">
                <XIcon weight="bold" className="size-5 text-foreground" />
              </SheetClose>
              <SheetTitle className="text-foreground">
                {t('Notifications')}
              </SheetTitle>
            </div>

            <Button
              variant="secondary"
              className="min-w-48 rounded-full bg-black/8 text-foreground hover:bg-black/12 dark:bg-white/10 dark:text-white dark:hover:bg-white/15"
              onClick={handleMarkAllAsRead}
              disabled={unreadCount === 0 || isMarkingAllRead}
            >
              {isMarkingAllRead ? (
                <LoaderIcon />
              ) : (
                <EnvelopeSimpleIcon weight="bold" className="size-5" />
              )}
              <span>{t('Mark all as read')}</span>
            </Button>
          </SheetHeader>

          <div
            ref={scrollContainerRef}
            className="flex flex-1 flex-col gap-2 overflow-y-auto px-6 pb-6"
          >
            {isLoading && notifications.length === 0 ? (
              <div className="flex flex-1 items-center justify-center gap-2 text-black/50 dark:text-white/50">
                <LoaderIcon />
                <span>{t('Loading...')}</span>
              </div>
            ) : notifications.length === 0 ? (
              <div className="flex flex-1 flex-col items-center justify-center gap-2 text-center text-black/50 dark:text-white/50">
                <BellSimpleIcon weight="fill" className="size-12 opacity-30" />
                <p className="text-sm">{t('No notifications yet')}</p>
              </div>
            ) : (
              <>
                {notifications.map((notification) => {
                  const isRead = !!notification?.is_read;
                  const config =
                    notificationTypeConfig[
                      notification?.type as keyof typeof notificationTypeConfig
                    ];
                  const Icon = config?.Icon;

                  return (
                    <button
                      key={notification?.id}
                      onClick={() => handleNotificationClick(notification)}
                      className={cn(
                        'cursor-pointer rounded-2xl border px-4 py-2.5 text-left transition-opacity hover:opacity-80',
                        isRead
                          ? 'border-black/8 bg-black/5 dark:border-white/8 dark:bg-white/5'
                          : (config?.container ?? 'border-black/8 bg-black/5'),
                      )}
                    >
                      <div className="mb-2.5 flex items-center gap-2">
                        {Icon && (
                          <Icon
                            weight="fill"
                            className={cn(
                              'size-6',
                              isRead
                                ? 'text-black/50 dark:text-white/50'
                                : config.icon,
                            )}
                          />
                        )}
                        <h4 className="font-medium text-foreground">
                          {notification.title}
                        </h4>
                        {!isRead && (
                          <span className="ml-auto h-2 w-2 shrink-0 rounded-full bg-primary" />
                        )}
                      </div>

                      <p className="font-medium text-black/50 dark:text-white/50">
                        {notification.message}
                      </p>

                      <Separator className="my-2.5 bg-black/10 dark:bg-white/10" />

                      <p className="font-medium text-black/50 dark:text-white/50">
                        {dayjs(notification.created_at).fromNow()}
                      </p>
                    </button>
                  );
                })}

                <div ref={sentinelRef} className="py-1">
                  {isLoadingMore && (
                    <div className="flex items-center justify-center gap-2 py-2 text-black/50 dark:text-white/50">
                      <LoaderIcon />
                      <span className="text-sm">{t('Loading more...')}</span>
                    </div>
                  )}
                  {!hasMore && notifications.length > 0 && (
                    <p className="py-2 text-center text-sm text-black/50 dark:text-white/50">
                      {t("You're all caught up!")}
                    </p>
                  )}
                </div>
              </>
            )}
          </div>
        </div>
      </SheetContent>
    </Sheet>
  );
}
