'use client';

import clsx from 'clsx';
import type { TOptions } from 'i18next';
import { observer } from 'mobx-react-lite';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useInView } from 'react-intersection-observer';

import ListHeading from '@/components/listView/ListHeading';
import ListItem from '@/components/listView/ListItem';
import SpinnerSVG from '@/components/svg/SpinnerSVG';
import { NotificationV2Schema } from '@/state/notificationsStore';

import NotificationBannerContainer from '../NotificationBannerContainer';
import NotificationV2ListItem from './NotificationV2ListItem';

export type Props = React.HTMLAttributes<HTMLDivElement> &
  Pick<
    React.ComponentProps<typeof NotificationV2ListItem>,
    'onDotClick' | 'onProfileClick' | 'onContentClick' | 'onFollowClick'
  > & {
    isLoading?: boolean;
    currentTime?: string;
    notifications: NotificationV2Schema[];
    onNotificationView?: React.ComponentProps<
      typeof NotificationV2ListItem
    >['onView'];
    onLoadMore?: () => void;
    hasMore?: boolean;
  };

type ListItemWrapper =
  | {
      type: 'heading';
      content: React.ReactNode;
    }
  | {
      type: 'notificationV2';
      content: NotificationV2Schema;
    };

function createDate(from?: string | number) {
  return from == null ? new Date() : new Date(from);
}

// Map headings to the oldest possible time they can use
const NOTIFICATION_HEADING_GROUPS: [
  key: string,
  threshold: (from?: string | number) => number,
  headingKey: string | [key: string, replaceKeys: TOptions],
][] = [
  [
    'today',
    (from) => createDate(from).setUTCHours(0, 0, 0, 0),
    'notification.today',
  ],
  [
    'yesterday',
    (from) => {
      const date = createDate(from);
      date.setDate(date.getDate() - 1);
      return date.setHours(0, 0, 0, 0);
    },
    'notification.yesterday',
  ],
  // [
  //   'pastWeek',
  //   (from) => {
  //     const date = createDate(from);
  //     date.setDate(date.getDate() - date.getDay()); // since Sunday
  //     return date.setHours(0, 0, 0, 0);
  //   },
  //   'notification.pastWeek',
  // ],
  [
    'past7Days',
    (from) => {
      const date = createDate(from);
      date.setDate(date.getDate() - 7); // last 7 days
      return date.setHours(0, 0, 0, 0);
    },
    ['notification.lastNDays', { count: 7 }],
  ],
  // [
  //   'lastWeek',
  //   (from) => {
  //     const date = createDate(from);
  //     date.setDate(date.getDate() - 7); // last 7 days
  //     date.setDate(date.getDate() - date.getDay() - 7); // since last Sunday
  //     return date.setHours(0, 0, 0, 0);
  //   },
  //   'notification.lastWeek',
  // ],
  // [
  //   'pastMonth',
  //   (from) => {
  //     const date = createDate(from);
  //     date.setDate(0);
  //     return date.setHours(0, 0, 0, 0);
  //   },
  //   'notification.pastMonth',
  // ],
  [
    'past30Days',
    (from) => {
      const date = createDate(from);
      date.setDate(date.getDate() - 30); // last 30 days
      return date.setHours(0, 0, 0, 0);
    },
    ['notification.lastNDays', { count: 30 }],
  ],
  ['older', () => 0, 'notification.older'],
];

const NotificationList: React.FC<Props> = observer((props) => {
  const {
    currentTime,
    notifications,
    isLoading,
    onDotClick,
    onProfileClick,
    onContentClick,
    onFollowClick,
    onNotificationView,
    onLoadMore,
    hasMore = true,
    ...restProps
  } = props;

  const { t } = useTranslation();

  // Replace the manual intersection observer with useInView
  const { ref: paginationRef } = useInView({
    onChange: (inView) => {
      if (inView && !isLoading && hasMore && onLoadMore) {
        onLoadMore();
      }
    },
  });

  // Inject headings
  const listItems = useMemo(() => {
    const listItems: ListItemWrapper[] = [];

    let headingIndex = -1;
    let currentHeading;
    let currentHeadingTime;
    for (const notification of notifications) {
      const notificationTime = new Date(notification.updated_at).getTime();

      // Move to the next relevant heading
      while (
        headingIndex < NOTIFICATION_HEADING_GROUPS.length - 1 &&
        (currentHeadingTime == null || notificationTime <= currentHeadingTime)
      ) {
        currentHeading = NOTIFICATION_HEADING_GROUPS[++headingIndex];
        currentHeadingTime = currentHeading[1](currentTime);
      }

      // Try to output the current heading
      if (currentHeading && notificationTime > currentHeadingTime!) {
        const [key, , headingKey] = currentHeading;
        listItems.push({
          type: 'heading',
          content: (
            <ListHeading
              className={clsx({ 'mt-4': listItems.length !== 0 })}
              key={key}
            >
              {Array.isArray(headingKey)
                ? t(headingKey[0], headingKey[1])
                : t(headingKey)}
            </ListHeading>
          ),
        });
        currentHeading = undefined;
      }

      listItems.push({
        type: 'notificationV2',
        content: notification,
      });
    }

    return listItems;
  }, [t, currentTime, notifications, notifications.length]);

  // Batched impression logging: log once per notification ID within the session
  // const impressedIds = useRef(new Set<string>());
  // useEffect(() => {
  //   if (onView) {
  //     const impressedNotifications = notifications
  //       .map((notification, index) => ({
  //         index,
  //         id: notification.id,
  //         type: notification.notification_type,
  //       }))
  //       .filter(({ id }) => {
  //         if (impressedIds.current.has(id)) {
  //           return false;
  //         }
  //         // Log one impression per notification
  //         impressedIds.current.add(id);
  //         return true;
  //       });
  //     // Log impressions if we have any new notifications
  //     if (impressedNotifications.length) {
  //       onView({
  //         notifications: impressedNotifications,
  //       });
  //     }
  //   }
  // }, [notifications, onView]);

  return (
    <div {...restProps}>
      <NotificationBannerContainer />
      {!listItems.length ? (
        <ListItem className='border-b-0 text-foreground-secondary'>
          <p className='grow text-center'>
            {isLoading ? (
              <SpinnerSVG className='inline-block' />
            ) : (
              t('notification.emptyState')
            )}
          </p>
        </ListItem>
      ) : (
        <>
          {listItems.map((listItem, index) => {
            switch (listItem.type) {
              case 'notificationV2':
                return (
                  <NotificationV2ListItem
                    key={listItem.content.id}
                    currentTime={currentTime}
                    notification={listItem.content}
                    onDotClick={
                      onDotClick &&
                      ((payload, e) => onDotClick({ ...payload, index }, e))
                    }
                    onProfileClick={
                      onProfileClick &&
                      ((payload, e) => onProfileClick({ ...payload, index }, e))
                    }
                    onContentClick={
                      onContentClick &&
                      ((payload, e) => onContentClick({ ...payload, index }, e))
                    }
                    onFollowClick={
                      onFollowClick &&
                      ((payload, e) => onFollowClick({ ...payload, index }, e))
                    }
                    onView={
                      onNotificationView &&
                      ((payload) => onNotificationView({ ...payload, index }))
                    }
                  />
                );
              default:
                return listItem.content;
            }
          })}
          {/* Invisible load more sentinel */}
          <div ref={paginationRef} className='h-px' aria-hidden='true' />
          {/* Loading indicator or end of list indicator */}
          <div className='pt-4 text-center'>
            {isLoading && hasMore ? (
              <SpinnerSVG className='inline-block' />
            ) : (
              !hasMore && (
                <div className='relative pt-4'>
                  <div className='pointer-events-none absolute inset-0' />
                  <p className='relative text-sm text-foreground-secondary/50'>
                    {t('notification.endOfList')}
                  </p>
                </div>
              )
            )}
          </div>
        </>
      )}
    </div>
  );
});

export default NotificationList;
