// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import * as React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { DurationInSeconds } from '../../util/durations/index.std.js';
import type { PropsType as TimelineItemProps } from './TimelineItem.dom.js';
import { TimelineItem } from './TimelineItem.dom.js';
import { UniversalTimerNotification } from './UniversalTimerNotification.dom.js';
import { CallMode } from '../../types/CallDisposition.std.js';
import { AvatarColors } from '../../types/Colors.std.js';
import { getDefaultConversation } from '../../test-helpers/getDefaultConversation.std.js';
import { WidthBreakpoint } from '../_util.std.js';
import { ThemeType } from '../../types/Util.std.js';
import { PaymentEventKind } from '../../types/Payment.std.js';
import { ErrorBoundary } from './ErrorBoundary.dom.js';
import { MessageInteractivity } from './Message.dom.js';

const { i18n } = window.SignalContext;

const renderReactionPicker: TimelineItemProps['renderReactionPicker'] = () => (
  <div />
);

const renderContact = (conversationId: string) => (
  <React.Fragment key={conversationId}>{conversationId}</React.Fragment>
);

const renderUniversalTimerNotification = () => (
  <UniversalTimerNotification
    i18n={i18n}
    expireTimer={DurationInSeconds.HOUR}
  />
);

const getDefaultProps = () => ({
  containerElementRef: React.createRef<HTMLElement>(),
  containerWidthBreakpoint: WidthBreakpoint.Wide,
  conversationId: 'conversation-id',
  getPreferredBadge: () => undefined,
  getSharedGroupNames: () => [],
  id: 'asdf',
  isNextItemCallingNotification: false,
  isPinned: false,
  isTargeted: false,
  isBlocked: false,
  isGroup: false,
  interactivity: MessageInteractivity.Normal,
  interactionMode: 'keyboard' as const,
  theme: ThemeType.light,
  platform: 'darwin',
  handleDebugMessage: action('handleDebugMessage'),
  targetMessage: action('targetMessage'),
  toggleSelectMessage: action('toggleSelectMessage'),
  endPoll: action('endPoll'),
  reactToMessage: action('reactToMessage'),
  checkForAccount: action('checkForAccount'),
  clearTargetedMessage: action('clearTargetedMessage'),
  setMessageToEdit: action('setMessageToEdit'),
  setQuoteByMessageId: action('setQuoteByMessageId'),
  copyMessageText: action('copyMessageText'),
  retryDeleteForEveryone: action('retryDeleteForEveryone'),
  retryMessageSend: action('retryMessageSend'),
  sendPollVote: action('sendPollVote'),
  blockGroupLinkRequests: action('blockGroupLinkRequests'),
  cancelAttachmentDownload: action('cancelAttachmentDownload'),
  kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
  markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'),
  messageExpanded: action('messageExpanded'),
  showConversation: action('showConversation'),
  openGiftBadge: action('openGiftBadge'),
  saveAttachment: action('saveAttachment'),
  saveAttachments: action('saveAttachments'),
  showPinMessageDialog: action('showPinMessageDialog'),
  onPinnedMessageRemove: action('onPinnedMessageRemove'),
  onOpenEditNicknameAndNoteModal: action('onOpenEditNicknameAndNoteModal'),
  onOutgoingAudioCallInConversation: action(
    'onOutgoingAudioCallInConversation'
  ),
  onOutgoingVideoCallInConversation: action(
    'onOutgoingVideoCallInConversation'
  ),
  pushPanelForConversation: action('pushPanelForConversation'),
  showContactModal: action('showContactModal'),
  showLightbox: action('showLightbox'),
  toggleDeleteMessagesModal: action('toggleDeleteMessagesModal'),
  toggleForwardMessagesModal: action('toggleForwardMessagesModal'),
  showLightboxForViewOnceMedia: action('showLightboxForViewOnceMedia'),
  doubleCheckMissingQuoteReference: action('doubleCheckMissingQuoteReference'),
  showAttachmentDownloadStillInProgressToast: action(
    'showAttachmentDownloadStillInProgressToast'
  ),
  showExpiredIncomingTapToViewToast: action(
    'showExpiredIncomingTapToViewToast'
  ),
  showExpiredOutgoingTapToViewToast: action(
    'showExpiredIncomingTapToViewToast'
  ),
  showMediaNoLongerAvailableToast: action('showMediaNoLongerAvailableToast'),
  showTapToViewNotAvailableModal: action('showTapToViewNotAvailableModal'),
  scrollToPinnedMessage: action('scrollToPinnedMessage'),
  scrollToPollMessage: action('scrollToPollMessage'),
  scrollToQuotedMessage: action('scrollToQuotedMessage'),
  showSpoiler: action('showSpoiler'),
  startConversation: action('startConversation'),
  returnToActiveCall: action('returnToActiveCall'),
  shouldCollapseAbove: false,
  shouldCollapseBelow: false,
  shouldHideMetadata: false,
  shouldRenderDateHeader: false,
  toggleSafetyNumberModal: action('toggleSafetyNumberModal'),

  now: Date.now(),

  renderContact,
  renderUniversalTimerNotification,
  renderReactionPicker,
  renderAudioAttachment: () => <div>*AudioAttachment*</div>,
  viewStory: action('viewStory'),

  onReplyToMessage: action('onReplyToMessage'),
  onOpenMessageRequestActionsConfirmation: action(
    'onOpenMessageRequestActionsConfirmation'
  ),
});

export default {
  title: 'Components/Conversation/TimelineItem',
} satisfies Meta<TimelineItemProps>;

export function PlainMessage(): React.JSX.Element {
  const item = {
    type: 'message',
    data: {
      id: 'id-1',
      direction: 'incoming',
      timestamp: Date.now(),
      author: {
        phoneNumber: '(202) 555-2001',
        color: AvatarColors[0],
      },
      text: '🔥',
    },
  } as TimelineItemProps['item'];

  return <TimelineItem {...getDefaultProps()} item={item} i18n={i18n} />;
}

export function Notification(): React.JSX.Element {
  const items = [
    {
      type: 'timerNotification',
      data: {
        phoneNumber: '(202) 555-0000',
        expireTimer: DurationInSeconds.MINUTE,
        ...getDefaultConversation(),
        type: 'fromOther',
      },
    },
    {
      type: 'timerNotification',
      data: {
        phoneNumber: '(202) 555-0000',
        disabled: true,
        ...getDefaultConversation(),
        type: 'fromOther',
      },
    },
    {
      type: 'universalTimerNotification',
      data: null,
    },
    {
      type: 'chatSessionRefreshed',
    },
    {
      type: 'contactRemovedNotification',
      data: null,
    },
    {
      type: 'safetyNumberNotification',
      data: {
        isGroup: false,
        contact: getDefaultConversation(),
      },
    },
    {
      type: 'deliveryIssue',
      data: {
        sender: getDefaultConversation(),
      },
    },
    {
      type: 'changeNumberNotification',
      data: {
        sender: getDefaultConversation(),
        timestamp: Date.now(),
      },
    },
    {
      type: 'titleTransitionNotification',
      data: {
        oldTitle: 'alice.01',
      },
    },
    {
      type: 'callHistory',
      data: {
        // declined incoming audio
        callMode: CallMode.Direct,
        wasDeclined: true,
        wasIncoming: true,
        wasVideoCall: false,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // declined incoming video
        callMode: CallMode.Direct,
        wasDeclined: true,
        wasIncoming: true,
        wasVideoCall: true,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // accepted incoming audio
        callMode: CallMode.Direct,
        acceptedTime: Date.now() - 300,
        wasDeclined: false,
        wasIncoming: true,
        wasVideoCall: false,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // accepted incoming video
        callMode: CallMode.Direct,
        acceptedTime: Date.now() - 400,
        wasDeclined: false,
        wasIncoming: true,
        wasVideoCall: true,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // missed (neither accepted nor declined) incoming audio
        callMode: CallMode.Direct,
        wasDeclined: false,
        wasIncoming: true,
        wasVideoCall: false,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // missed (neither accepted nor declined) incoming video
        callMode: CallMode.Direct,
        wasDeclined: false,
        wasIncoming: true,
        wasVideoCall: true,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // accepted outgoing audio
        callMode: CallMode.Direct,
        acceptedTime: Date.now() - 200,
        wasDeclined: false,
        wasIncoming: false,
        wasVideoCall: false,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // accepted outgoing video
        callMode: CallMode.Direct,
        acceptedTime: Date.now() - 200,
        wasDeclined: false,
        wasIncoming: false,
        wasVideoCall: true,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // declined outgoing audio
        callMode: CallMode.Direct,
        wasDeclined: true,
        wasIncoming: false,
        wasVideoCall: false,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // declined outgoing video
        callMode: CallMode.Direct,
        wasDeclined: true,
        wasIncoming: false,
        wasVideoCall: true,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // unanswered (neither accepted nor declined) outgoing audio
        callMode: CallMode.Direct,
        wasDeclined: false,
        wasIncoming: false,
        wasVideoCall: false,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // unanswered (neither accepted nor declined) outgoing video
        callMode: CallMode.Direct,
        wasDeclined: false,
        wasIncoming: false,
        wasVideoCall: true,
        endedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // ongoing group call
        callMode: CallMode.Group,
        conversationId: 'abc123',
        creator: {
          firstName: 'Luigi',
          isMe: false,
          title: 'Luigi Mario',
        },
        ended: false,
        deviceCount: 1,
        maxDevices: 16,
        startedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // ongoing group call started by you
        callMode: CallMode.Group,
        conversationId: 'abc123',
        creator: {
          firstName: 'Peach',
          isMe: true,
          title: 'Princess Peach',
        },
        ended: false,
        deviceCount: 1,
        maxDevices: 16,
        startedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // ongoing group call, creator unknown
        callMode: CallMode.Group,
        conversationId: 'abc123',
        ended: false,
        deviceCount: 1,
        maxDevices: 16,
        startedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // ongoing and active group call
        callMode: CallMode.Group,
        activeCallConversationId: 'abc123',
        conversationId: 'abc123',
        creator: {
          firstName: 'Luigi',
          isMe: false,
          title: 'Luigi Mario',
        },
        ended: false,
        deviceCount: 1,
        maxDevices: 16,
        startedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // ongoing group call, but you're in another one
        callMode: CallMode.Group,
        activeCallConversationId: 'abc123',
        conversationId: 'xyz987',
        creator: {
          firstName: 'Luigi',
          isMe: false,
          title: 'Luigi Mario',
        },
        ended: false,
        deviceCount: 1,
        maxDevices: 16,
        startedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // ongoing full group call
        callMode: CallMode.Group,
        conversationId: 'abc123',
        creator: {
          firstName: 'Luigi',
          isMe: false,
          title: 'Luigi Mario',
        },
        ended: false,
        deviceCount: 16,
        maxDevices: 16,
        startedTime: Date.now(),
      },
    },
    {
      type: 'callHistory',
      data: {
        // finished call
        callMode: CallMode.Group,
        conversationId: 'abc123',
        creator: {
          firstName: 'Luigi',
          isMe: false,
          title: 'Luigi Mario',
        },
        ended: true,
        deviceCount: 0,
        maxDevices: 16,
        startedTime: Date.now(),
      },
    },
    {
      type: 'profileChange',
      data: {
        change: {
          type: 'name',
          oldName: 'Fred',
          newName: 'John',
        },
        changedContact: getDefaultConversation(),
      },
    },
    {
      type: 'paymentEvent',
      data: {
        event: {
          kind: PaymentEventKind.ActivationRequest,
        },
        sender: getDefaultConversation(),
        conversation: getDefaultConversation(),
      },
    },
    {
      type: 'paymentEvent',
      data: {
        event: {
          kind: PaymentEventKind.Activation,
        },
        sender: getDefaultConversation(),
        conversation: getDefaultConversation(),
      },
    },
    {
      type: 'paymentEvent',
      data: {
        event: {
          kind: PaymentEventKind.ActivationRequest,
        },
        sender: getDefaultConversation({
          isMe: true,
        }),
        conversation: getDefaultConversation(),
      },
    },
    {
      type: 'paymentEvent',
      data: {
        event: {
          kind: PaymentEventKind.Activation,
        },
        sender: getDefaultConversation({
          isMe: true,
        }),
        conversation: getDefaultConversation(),
      },
    },
    {
      type: 'pinnedMessage',
      data: {
        sender: getDefaultConversation({ isMe: true }),
        pinnedMessageId: '1',
      },
    },
    {
      type: 'pinnedMessage',
      data: {
        sender: getDefaultConversation({ isMe: false }),
        pinnedMessageId: '1',
      },
    },
    {
      type: 'resetSessionNotification',
      data: null,
    },
    {
      type: 'unsupportedMessage',
      data: {
        canProcessNow: true,
        contact: getDefaultConversation(),
      },
    },
    {
      type: 'unsupportedMessage',
      data: {
        canProcessNow: false,
        contact: getDefaultConversation(),
      },
    },
    {
      type: 'verificationNotification',
      data: {
        type: 'markVerified',
        isLocal: false,
        contact: getDefaultConversation(),
      },
    },
    {
      type: 'verificationNotification',
      data: {
        type: 'markVerified',
        isLocal: true,
        contact: getDefaultConversation(),
      },
    },
    {
      type: 'verificationNotification',
      data: {
        type: 'markNotVerified',
        isLocal: false,
        contact: getDefaultConversation(),
      },
    },
    {
      type: 'verificationNotification',
      data: {
        type: 'markNotVerified',
        isLocal: true,
        contact: getDefaultConversation(),
      },
    },
    {
      type: 'conversationMerge',
      data: {
        conversationTitle: 'Alice',
        obsoleteConversationTitle: 'Nancy',
        obsoleteConversationNumber: '+121255501234',
      },
    },
  ];

  return (
    <>
      {items.map((item, index) => (
        <React.Fragment key={index}>
          <TimelineItem
            {...getDefaultProps()}
            item={item as TimelineItemProps['item']}
            i18n={i18n}
          />
        </React.Fragment>
      ))}
    </>
  );
}

export function UnknownType(): React.JSX.Element {
  const item = {
    type: 'random',
    data: {
      somethin: 'somethin',
    },
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
  } as any as TimelineItemProps['item'];

  return (
    <ErrorBoundary i18n={i18n} showDebugLog={action('showDebugLog')}>
      <TimelineItem {...getDefaultProps()} item={item} i18n={i18n} />
    </ErrorBoundary>
  );
}

export function MissingItem(): React.JSX.Element {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const item = null as any as TimelineItemProps['item'];

  return <TimelineItem {...getDefaultProps()} item={item} i18n={i18n} />;
}
