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

import type { Meta, StoryFn } from '@storybook/react';
import React, { useRef, useState } from 'react';

import { action } from '@storybook/addon-actions';
import { Page, Preferences } from './Preferences';
import { DEFAULT_CONVERSATION_COLOR } from '../types/Colors';
import { PhoneNumberSharingMode } from '../util/phoneNumberSharingMode';
import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability';
import { EmojiSkinTone } from './fun/data/emojis';
import { DAY, DurationInSeconds, WEEK } from '../util/durations';
import { DialogUpdate } from './DialogUpdate';
import { DialogType } from '../types/Dialogs';
import { ThemeType } from '../types/Util';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
import { EditState, ProfileEditor } from './ProfileEditor';
import {
  UsernameEditState,
  UsernameLinkState,
} from '../state/ducks/usernameEnums';

import type { PropsType } from './Preferences';
import type { WidthBreakpoint } from './_util';
import type { MessageAttributesType } from '../model-types';

const { i18n } = window.SignalContext;

const me = {
  ...getDefaultConversation(),
  phoneNumber: '(215) 555-2345',
  username: 'someone.243',
};

const availableMicrophones = [
  {
    name: 'DefAuLt (Headphones)',
    index: 0,
    uniqueId: 'Default',
    i18nKey: 'default_communication_device',
  },
];

const availableSpeakers = [
  {
    name: 'Default',
    index: 0,
    uniqueId: 'Default',
    i18nKey: 'default_communication_device',
  },
  {
    name: "Natalie's Airpods (Bluetooth)",
    index: 1,
    uniqueId: 'aa',
  },
  {
    name: 'UE Boom (Bluetooth)',
    index: 2,
    uniqueId: 'bb',
  },
];

const validateBackupResult = {
  totalBytes: 100,
  duration: 10000,
  stats: {
    adHocCalls: 1,
    callLinks: 2,
    conversations: 3,
    chats: 4,
    distributionLists: 5,
    messages: 6,
    notificationProfiles: 2,
    skippedMessages: 7,
    stickerPacks: 8,
    fixedDirectMessages: 9,
  },
};

const exportLocalBackupResult = {
  ...validateBackupResult,
  snapshotDir: '/home/signaluser/SignalBackups/signal-backup-1745618069169',
};

function renderUpdateDialog(
  props: Readonly<{ containerWidthBreakpoint: WidthBreakpoint }>
): JSX.Element {
  return (
    <DialogUpdate
      i18n={i18n}
      containerWidthBreakpoint={props.containerWidthBreakpoint}
      dialogType={DialogType.DownloadReady}
      downloadSize={100000}
      downloadedSize={50000}
      version="8.99.0"
      currentVersion="8.98.00"
      disableDismiss
      dismissDialog={action('dismissDialog')}
      snoozeUpdate={action('snoozeUpdate')}
      startUpdate={action('startUpdate')}
    />
  );
}
function RenderProfileEditor(): JSX.Element {
  const contentsRef = useRef<HTMLDivElement | null>(null);
  return (
    <ProfileEditor
      aboutEmoji={undefined}
      aboutText={undefined}
      color={undefined}
      contentsRef={contentsRef}
      conversationId="something"
      deleteAvatarFromDisk={action('deleteAvatarFromDisk')}
      deleteUsername={action('deleteUsername')}
      familyName={me.familyName}
      firstName={me.firstName ?? ''}
      hasCompletedUsernameLinkOnboarding={false}
      i18n={i18n}
      editState={EditState.None}
      markCompletedUsernameLinkOnboarding={action(
        'markCompletedUsernameLinkOnboarding'
      )}
      onProfileChanged={action('onProfileChanged')}
      onEmojiSkinToneDefaultChange={action('onEmojiSkinToneDefaultChange')}
      openUsernameReservationModal={action('openUsernameReservationModal')}
      profileAvatarUrl={undefined}
      recentEmojis={[]}
      renderUsernameEditor={() => <div />}
      replaceAvatar={action('replaceAvatar')}
      resetUsernameLink={action('resetUsernameLink')}
      saveAttachment={action('saveAttachment')}
      saveAvatarToDisk={action('saveAvatarToDisk')}
      setEditState={action('setEditState')}
      setUsernameEditState={action('setUsernameEditState')}
      setUsernameLinkColor={action('setUsernameLinkColor')}
      showToast={action('showToast')}
      emojiSkinToneDefault={null}
      userAvatarData={[]}
      username={undefined}
      usernameCorrupted={false}
      usernameEditState={UsernameEditState.Editing}
      usernameLink={undefined}
      usernameLinkColor={undefined}
      usernameLinkCorrupted={false}
      usernameLinkState={UsernameLinkState.Ready}
    />
  );
}

function renderToastManager(): JSX.Element {
  return <div />;
}

export default {
  title: 'Components/Preferences',
  component: Preferences,
  args: {
    i18n,

    autoDownloadAttachment: {
      photos: true,
      videos: false,
      audio: false,
      documents: false,
    },
    availableCameras: [
      {
        deviceId:
          'dfbe6effe70b0611ba0fdc2a9ea3f39f6cb110e6687948f7e5f016c111b7329c',
        groupId:
          '63ee218d2446869e40adfc958ff98263e51f74382b0143328ee4826f20a76f47',
        kind: 'videoinput' as MediaDeviceKind,
        label: 'FaceTime HD Camera (Built-in) (9fba:bced)',
      },
      {
        deviceId:
          'e2db196a31d50ff9b135299dc0beea67f65b1a25a06d8a4ce76976751bb7a08d',
        groupId:
          '218ba7f00d7b1239cca15b9116769e5e7d30cc01104ebf84d667643661e0ecf9',
        kind: 'videoinput' as MediaDeviceKind,
        label: 'Logitech Webcam (4e72:9058)',
      },
    ],
    availableLocales: ['en'],
    availableMicrophones,
    availableSpeakers,
    backupFeatureEnabled: false,
    badge: undefined,
    blockedCount: 0,
    customColors: {},
    defaultConversationColor: DEFAULT_CONVERSATION_COLOR,
    deviceName: 'Work Windows ME',
    emojiSkinToneDefault: EmojiSkinTone.None,
    phoneNumber: '+1 555 123-4567',
    hasAudioNotifications: true,
    hasAutoConvertEmoji: true,
    hasAutoDownloadUpdate: true,
    hasAutoLaunch: true,
    hasCallNotifications: true,
    hasCallRingtoneNotification: false,
    hasContentProtection: false,
    hasCountMutedConversations: false,
    hasFailedStorySends: false,
    hasHideMenuBar: false,
    hasIncomingCallNotifications: true,
    hasLinkPreviews: true,
    hasMediaCameraPermissions: true,
    hasMediaPermissions: true,
    hasMessageAudio: true,
    hasMinimizeToAndStartInSystemTray: true,
    hasMinimizeToSystemTray: true,
    hasNotificationAttention: false,
    hasNotifications: true,
    hasPendingUpdate: false,
    hasReadReceipts: true,
    hasRelayCalls: false,
    hasSpellCheck: true,
    hasStoriesDisabled: false,
    hasTextFormatting: true,
    hasTypingIndicators: true,
    initialSpellCheckSetting: true,
    isAutoDownloadUpdatesSupported: true,
    isAutoLaunchSupported: true,
    isHideMenuBarSupported: true,
    isNotificationAttentionSupported: true,
    isSyncSupported: true,
    isSystemTraySupported: true,
    isInternalUser: false,
    isContentProtectionSupported: true,
    isContentProtectionNeeded: true,
    isMinimizeToAndStartInSystemTraySupported: true,
    isUpdateDownloaded: false,
    lastSyncTime: Date.now(),
    localeOverride: null,
    me,
    navTabsCollapsed: false,
    notificationContent: 'name',
    otherTabsUnreadStats: {
      unreadCount: 0,
      unreadMentionsCount: 0,
      markedUnread: false,
    },
    page: Page.Profile,
    preferredSystemLocales: ['en'],
    preferredWidthFromStorage: 300,
    resolvedLocale: 'en',
    selectedCamera:
      'dfbe6effe70b0611ba0fdc2a9ea3f39f6cb110e6687948f7e5f016c111b7329c',
    selectedMicrophone: availableMicrophones[0],
    selectedSpeaker: availableSpeakers[1],
    sentMediaQualitySetting: 'standard',
    themeSetting: 'system',
    theme: ThemeType.light,
    universalExpireTimer: DurationInSeconds.HOUR,
    whoCanFindMe: PhoneNumberDiscoverability.Discoverable,
    whoCanSeeMe: PhoneNumberSharingMode.Everybody,
    zoomFactor: 1,

    renderProfileEditor: RenderProfileEditor,
    renderToastManager,
    renderUpdateDialog,
    getConversationsWithCustomColor: () => [],

    addCustomColor: action('addCustomColor'),
    doDeleteAllData: action('doDeleteAllData'),
    editCustomColor: action('editCustomColor'),
    exportLocalBackup: async () => {
      return {
        result: exportLocalBackupResult,
      };
    },
    getMessageCountBySchemaVersion: async () => [
      { schemaVersion: 10, count: 1024 },
      { schemaVersion: 8, count: 256 },
    ],
    getMessageSampleForSchemaVersion: async () => [
      { id: 'messageId' } as MessageAttributesType,
    ],
    makeSyncRequest: action('makeSyncRequest'),
    onAudioNotificationsChange: action('onAudioNotificationsChange'),
    onAutoConvertEmojiChange: action('onAutoConvertEmojiChange'),
    onAutoDownloadAttachmentChange: action('onAutoDownloadAttachmentChange'),
    onAutoDownloadUpdateChange: action('onAutoDownloadUpdateChange'),
    onAutoLaunchChange: action('onAutoLaunchChange'),
    onCallNotificationsChange: action('onCallNotificationsChange'),
    onCallRingtoneNotificationChange: action(
      'onCallRingtoneNotificationChange'
    ),
    onContentProtectionChange: action('onContentProtectionChange'),
    onCountMutedConversationsChange: action('onCountMutedConversationsChange'),
    onEmojiSkinToneDefaultChange: action('onEmojiSkinToneDefaultChange'),
    onHasStoriesDisabledChanged: action('onHasStoriesDisabledChanged'),
    onHideMenuBarChange: action('onHideMenuBarChange'),
    onIncomingCallNotificationsChange: action(
      'onIncomingCallNotificationsChange'
    ),
    onLocaleChange: action('onLocaleChange'),
    onLastSyncTimeChange: action('onLastSyncTimeChange'),
    onMediaCameraPermissionsChange: action('onMediaCameraPermissionsChange'),
    onMediaPermissionsChange: action('onMediaPermissionsChange'),
    onMessageAudioChange: action('onMessageAudioChange'),
    onMinimizeToAndStartInSystemTrayChange: action(
      'onMinimizeToAndStartInSystemTrayChange'
    ),
    onMinimizeToSystemTrayChange: action('onMinimizeToSystemTrayChange'),
    onNotificationAttentionChange: action('onNotificationAttentionChange'),
    onNotificationContentChange: action('onNotificationContentChange'),
    onNotificationsChange: action('onNotificationsChange'),
    onRelayCallsChange: action('onRelayCallsChange'),
    onSelectedCameraChange: action('onSelectedCameraChange'),
    onSelectedMicrophoneChange: action('onSelectedMicrophoneChange'),
    onSelectedSpeakerChange: action('onSelectedSpeakerChange'),
    onSentMediaQualityChange: action('onSentMediaQualityChange'),
    onSpellCheckChange: action('onSpellCheckChange'),
    onStartUpdate: action('onStartUpdate'),
    onTextFormattingChange: action('onTextFormattingChange'),
    onThemeChange: action('onThemeChange'),
    onToggleNavTabsCollapse: action('onToggleNavTabsCollapse'),
    onUniversalExpireTimerChange: action('onUniversalExpireTimerChange'),
    onWhoCanSeeMeChange: action('onWhoCanSeeMeChange'),
    onWhoCanFindMeChange: action('onWhoCanFindMeChange'),
    onZoomFactorChange: action('onZoomFactorChange'),
    refreshCloudBackupStatus: action('refreshCloudBackupStatus'),
    refreshBackupSubscriptionStatus: action('refreshBackupSubscriptionStatus'),
    removeCustomColor: action('removeCustomColor'),
    removeCustomColorOnConversations: action(
      'removeCustomColorOnConversations'
    ),
    resetAllChatColors: action('resetAllChatColors'),
    resetDefaultChatColor: action('resetDefaultChatColor'),
    savePreferredLeftPaneWidth: action('savePreferredLeftPaneWidth'),
    setGlobalDefaultConversationColor: action(
      'setGlobalDefaultConversationColor'
    ),
    setPage: action('setPage'),
    showToast: action('showToast'),
    validateBackup: async () => {
      return {
        result: validateBackupResult,
      };
    },
  } satisfies PropsType,
} satisfies Meta<PropsType>;

// eslint-disable-next-line react/function-component-definition
const Template: StoryFn<PropsType> = args => {
  const [page, setPage] = useState(args.page);
  return <Preferences {...args} page={page} setPage={setPage} />;
};

export const _Preferences = Template.bind({});

export const General = Template.bind({});
General.args = {
  page: Page.General,
};
export const Appearance = Template.bind({});
Appearance.args = {
  page: Page.Appearance,
};
export const Chats = Template.bind({});
Chats.args = {
  page: Page.Chats,
};
export const Calls = Template.bind({});
Calls.args = {
  page: Page.Calls,
};
export const Notifications = Template.bind({});
Notifications.args = {
  page: Page.Notifications,
};
export const Privacy = Template.bind({});
Privacy.args = {
  page: Page.Privacy,
};
export const DataUsage = Template.bind({});
DataUsage.args = {
  page: Page.DataUsage,
};
export const Internal = Template.bind({});
Internal.args = {
  page: Page.Internal,
  isInternalUser: true,
};

export const Blocked1 = Template.bind({});
Blocked1.args = {
  blockedCount: 1,
  page: Page.Privacy,
};

export const BlockedMany = Template.bind({});
BlockedMany.args = {
  blockedCount: 55,
  page: Page.Privacy,
};

export const CustomUniversalExpireTimer = Template.bind({});
CustomUniversalExpireTimer.args = {
  universalExpireTimer: DurationInSeconds.fromSeconds(9000),
  page: Page.Privacy,
};

export const PNPSharingDisabled = Template.bind({});
PNPSharingDisabled.args = {
  whoCanSeeMe: PhoneNumberSharingMode.Nobody,
  whoCanFindMe: PhoneNumberDiscoverability.Discoverable,
  page: Page.PNP,
};

export const PNPDiscoverabilityDisabled = Template.bind({});
PNPDiscoverabilityDisabled.args = {
  whoCanSeeMe: PhoneNumberSharingMode.Nobody,
  whoCanFindMe: PhoneNumberDiscoverability.NotDiscoverable,
  page: Page.PNP,
};

export const BackupsPaidActive = Template.bind({});
BackupsPaidActive.args = {
  page: Page.Backups,
  backupFeatureEnabled: true,
  cloudBackupStatus: {
    mediaSize: 539_249_410_039,
    protoSize: 100_000_000,
    createdAt: new Date(Date.now() - WEEK).getTime(),
  },
  backupSubscriptionStatus: {
    status: 'active',
    cost: {
      amount: 22.99,
      currencyCode: 'USD',
    },
    renewalDate: new Date(Date.now() + 20 * DAY),
  },
};

export const BackupsPaidCancelled = Template.bind({});
BackupsPaidCancelled.args = {
  page: Page.Backups,
  backupFeatureEnabled: true,
  cloudBackupStatus: {
    mediaSize: 539_249_410_039,
    protoSize: 100_000_000,
    createdAt: new Date(Date.now() - WEEK).getTime(),
  },
  backupSubscriptionStatus: {
    status: 'pending-cancellation',
    cost: {
      amount: 22.99,
      currencyCode: 'USD',
    },
    expiryDate: new Date(Date.now() + 20 * DAY),
  },
};

export const BackupsFree = Template.bind({});
BackupsFree.args = {
  page: Page.Backups,
  backupFeatureEnabled: true,
  backupSubscriptionStatus: {
    status: 'free',
    mediaIncludedInBackupDurationDays: 30,
  },
};

export const BackupsOff = Template.bind({});
BackupsOff.args = {
  backupFeatureEnabled: true,
};

export const BackupsSubscriptionNotFound = Template.bind({});
BackupsSubscriptionNotFound.args = {
  page: Page.Backups,
  backupFeatureEnabled: true,
  backupSubscriptionStatus: {
    status: 'not-found',
  },
  cloudBackupStatus: {
    mediaSize: 539_249_410_039,
    protoSize: 100_000_000,
    createdAt: new Date(Date.now() - WEEK).getTime(),
  },
};

export const BackupsSubscriptionExpired = Template.bind({});
BackupsSubscriptionExpired.args = {
  page: Page.Backups,
  backupFeatureEnabled: true,
  backupSubscriptionStatus: {
    status: 'expired',
  },
};

export const UpdateAvailable = Template.bind({});
UpdateAvailable.args = {
  hasPendingUpdate: true,
};

export const UpdateDownloaded = Template.bind({});
UpdateDownloaded.args = {
  isUpdateDownloaded: true,
};

export const NavTabsCollapsed = Template.bind({});
NavTabsCollapsed.args = {
  navTabsCollapsed: true,
};

export const NavTabsCollapsedWithBadges = Template.bind({});
NavTabsCollapsedWithBadges.args = {
  navTabsCollapsed: true,
  hasFailedStorySends: false,
  otherTabsUnreadStats: {
    unreadCount: 1,
    unreadMentionsCount: 2,
    markedUnread: false,
  },
};

export const NavTabsCollapsedWithExclamation = Template.bind({});
NavTabsCollapsedWithExclamation.args = {
  navTabsCollapsed: true,
  hasFailedStorySends: true,
  otherTabsUnreadStats: {
    unreadCount: 1,
    unreadMentionsCount: 2,
    markedUnread: true,
  },
};
