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

import type { ChangeEvent, JSX } from 'react';
import { useCallback, useMemo, useState, useRef } from 'react';
import lodash from 'lodash';
import classNames from 'classnames';

import type { LocalizerType } from '../types/I18N.std.ts';
import {
  FlowingSettingsControl as FlowingControl,
  SettingsRow,
} from './PreferencesUtil.dom.tsx';
import { SIGNAL_BACKUPS_LEARN_MORE_URL } from './PreferencesBackups.dom.tsx';
import { I18n } from './I18n.dom.tsx';
import type { SettingsLocation } from '../types/Nav.std.ts';
import { SettingsPage } from '../types/Nav.std.ts';
import { ToastType } from '../types/Toast.dom.tsx';
import type { ShowToastAction } from '../state/ducks/toast.preload.ts';
import { Modal } from './Modal.dom.tsx';
import type {
  PromptOSAuthReasonType,
  PromptOSAuthResultType,
} from '../util/os/promptOSAuthMain.main.ts';
import { AxoButton } from '../axo/AxoButton.dom.tsx';
import { AxoDialog } from '../axo/AxoDialog.dom.tsx';
import { AxoCheckbox } from '../axo/AxoCheckbox.dom.tsx';
import { SECOND } from '../util/durations/constants.std.ts';
import { formatBackupKeyForDisplay } from '../util/formatBackupKeyForDisplay.std.ts';
import { formatTimestamp } from '../util/formatTimestamp.dom.ts';
import type { LocalBackupExportMetadata } from '../types/LocalExport.std.ts';
import { tw } from '../axo/tw.dom.tsx';
import { createLogger } from '../logging/log.std.ts';
import { toLogFormat } from '../types/errors.std.ts';
import { AxoAlertDialog } from '../axo/AxoAlertDialog.dom.tsx';
import { AxoSymbol } from '../axo/AxoSymbol.dom.tsx';

const { noop } = lodash;
const log = createLogger('PreferencesLocalBackups');

const SIGNAL_USER_SAFETY_LINK =
  'https://support.signal.org/hc/articles/9932566320410-Staying-Safe-from-Phishing-Scams-and-Impersonation';

export function PreferencesLocalBackups({
  backupKey,
  backupKeyHash,
  disableLocalBackups,
  i18n,
  lastLocalBackup,
  localBackupFolder,
  openFileInFolder,
  osName,
  onBackupKeyViewed,
  settingsLocation,
  pickLocalBackupFolder,
  previouslyViewedBackupKeyHash,
  promptOSAuth,
  setSettingsLocation,
  showToast,
  startLocalBackupExport,
}: {
  backupKey: string;
  backupKeyHash: string;
  disableLocalBackups: ({
    deleteExistingBackups,
  }: {
    deleteExistingBackups: boolean;
  }) => Promise<void>;
  i18n: LocalizerType;
  lastLocalBackup: LocalBackupExportMetadata | undefined;
  localBackupFolder: string | undefined;
  onBackupKeyViewed: ({ backupKeyHash }: { backupKeyHash: string }) => void;
  openFileInFolder: (path: string) => void;
  osName: 'linux' | 'macos' | 'windows' | undefined;
  settingsLocation: SettingsLocation;
  pickLocalBackupFolder: () => Promise<string | undefined>;
  previouslyViewedBackupKeyHash: string | undefined;
  promptOSAuth: (
    reason: PromptOSAuthReasonType
  ) => Promise<PromptOSAuthResultType>;
  setSettingsLocation: (settingsLocation: SettingsLocation) => void;
  showToast: ShowToastAction;
  startLocalBackupExport: () => void;
}): JSX.Element | null {
  const [authError, setAuthError] =
    useState<Exclude<PromptOSAuthResultType, 'success' | 'unsupported'>>();
  const [isAuthPending, setIsAuthPending] = useState<boolean>(false);
  const [isDisablePending, setIsDisablePending] = useState<boolean>(false);
  const [isShowingBackupKeyChangedModal, setIsShowingBackupKeyChangedModal] =
    useState<boolean>(false);

  if (settingsLocation.page === SettingsPage.LocalBackupsSetupFolder) {
    if (localBackupFolder) {
      setSettingsLocation({ page: SettingsPage.LocalBackups });
    }
    return (
      <LocalBackupsSetupFolderPicker
        i18n={i18n}
        pickLocalBackupFolder={async () => {
          const folder = await pickLocalBackupFolder();
          if (folder) {
            setSettingsLocation({ page: SettingsPage.LocalBackupsSetupKey });
          }
        }}
      />
    );
  }

  if (
    settingsLocation.page === SettingsPage.LocalBackupsSetupKey ||
    settingsLocation.page === SettingsPage.LocalBackupsKeyReference
  ) {
    return (
      <LocalBackupsBackupKeyViewer
        backupKey={backupKey}
        i18n={i18n}
        isReferencing={
          settingsLocation.page === SettingsPage.LocalBackupsKeyReference &&
          previouslyViewedBackupKeyHash === backupKeyHash
        }
        onBackupKeyViewed={() => {
          onBackupKeyViewed({ backupKeyHash });
          setSettingsLocation({
            page: SettingsPage.LocalBackups,
          });
        }}
        showToast={showToast}
      />
    );
  }

  if (!localBackupFolder && !isDisablePending) {
    setSettingsLocation({ page: SettingsPage.LocalBackupsSetupFolder });
    return null;
  }

  if (!previouslyViewedBackupKeyHash && !isDisablePending) {
    setSettingsLocation({ page: SettingsPage.LocalBackupsSetupKey });
    return null;
  }

  async function showKeyReferenceWithAuth() {
    setAuthError(undefined);

    try {
      setIsAuthPending(true);
      const result = await promptOSAuth('view-aep');
      if (result === 'success' || result === 'unsupported') {
        setSettingsLocation({
          page: SettingsPage.LocalBackupsKeyReference,
        });
      } else {
        setAuthError(result);
      }
    } catch (e) {
      log.error(
        'Error thrown when requesting OS auth for viewing AEP',
        toLogFormat(e)
      );
      setAuthError('error');
    } finally {
      setIsAuthPending(false);
    }
  }

  const learnMoreLink = (parts: Array<string | JSX.Element>) => (
    <a href={SIGNAL_BACKUPS_LEARN_MORE_URL} rel="noreferrer" target="_blank">
      {parts}
    </a>
  );

  const lastBackupText = lastLocalBackup
    ? formatTimestamp(lastLocalBackup.timestamp, {
        dateStyle: 'medium',
        timeStyle: 'short',
      })
    : i18n('icu:Preferences__local-backups-last-backup-never');

  let showInFolderText = i18n(
    'icu:PlaintextExport--CompleteDialog--ShowFiles--Windows'
  );
  if (osName === 'macos') {
    showInFolderText = i18n(
      'icu:PlaintextExport--CompleteDialog--ShowFiles--Mac'
    );
  } else if (osName === 'linux') {
    showInFolderText = i18n(
      'icu:PlaintextExport--CompleteDialog--ShowFiles--Linux'
    );
  }
  return (
    <>
      <div className="Preferences__padding">
        <div className="Preferences__description Preferences__description--medium">
          {i18n('icu:Preferences__local-backups-section__description')}
        </div>
      </div>
      <SettingsRow className="Preferences--BackupsRow">
        <FlowingControl>
          <div className="Preferences__two-thirds-flow">
            <label>
              {i18n('icu:Preferences__local-backups-last-backup')}
              <div className="Preferences__description">{lastBackupText}</div>
            </label>
          </div>
          <div
            className={classNames(
              'Preferences__flow-button',
              'Preferences__one-third-flow',
              'Preferences__one-third-flow--align-right'
            )}
          >
            <AxoButton.Root
              variant="strong-secondary"
              size="lg"
              onClick={async () => {
                if (
                  !previouslyViewedBackupKeyHash ||
                  previouslyViewedBackupKeyHash !== backupKeyHash
                ) {
                  setIsShowingBackupKeyChangedModal(true);
                } else {
                  startLocalBackupExport();
                }
              }}
            >
              {i18n('icu:Preferences__local-backups-backup-now')}
            </AxoButton.Root>
          </div>
        </FlowingControl>
        <FlowingControl>
          <div className="Preferences__two-thirds-flow">
            <label>
              {i18n('icu:Preferences__local-backups-folder')}
              <div className="Preferences__description">
                {localBackupFolder}
              </div>
            </label>
          </div>
          <div
            className={classNames(
              'Preferences__flow-button',
              'Preferences__one-third-flow',
              'Preferences__one-third-flow--align-right'
            )}
          >
            <AxoButton.Root
              variant="strong-secondary"
              size="lg"
              onClick={() =>
                localBackupFolder
                  ? openFileInFolder(localBackupFolder)
                  : undefined
              }
            >
              {showInFolderText}
            </AxoButton.Root>
          </div>
        </FlowingControl>
        <FlowingControl>
          <div className="Preferences__two-thirds-flow">
            <label>
              {i18n('icu:Preferences__recovery-key')}
              <div className="Preferences__description">
                {i18n('icu:Preferences__recovery-key-description')}
              </div>
            </label>
          </div>
          <div
            className={classNames(
              'Preferences__flow-button',
              'Preferences__one-third-flow',
              'Preferences__one-third-flow--align-right'
            )}
          >
            <AxoButton.Root
              variant="strong-secondary"
              size="lg"
              pending={isAuthPending}
              onClick={async () => {
                if (
                  !previouslyViewedBackupKeyHash ||
                  previouslyViewedBackupKeyHash !== backupKeyHash
                ) {
                  setIsShowingBackupKeyChangedModal(true);
                } else {
                  await showKeyReferenceWithAuth();
                }
              }}
            >
              {i18n('icu:Preferences__view-key')}
            </AxoButton.Root>
          </div>
        </FlowingControl>
        <FlowingControl>
          <div className="Preferences__two-thirds-flow">
            <label>{i18n('icu:Preferences__local-backups-turn-off')}</label>
          </div>
          <div
            className={classNames(
              'Preferences__flow-button',
              'Preferences__one-third-flow',
              'Preferences__one-third-flow--align-right'
            )}
          >
            <AxoButton.Root
              variant="subtle-destructive"
              size="lg"
              onClick={() => {
                setIsDisablePending(true);
              }}
            >
              {i18n('icu:Preferences__local-backups-turn-off-action')}
            </AxoButton.Root>
          </div>
        </FlowingControl>
      </SettingsRow>
      <SettingsRow className="Preferences--BackupsRow">
        <div className="Preferences__padding">
          <div className="Preferences__description Preferences__description--medium">
            <I18n
              id="icu:Preferences--local-backups-restore-info"
              i18n={i18n}
              components={{
                learnMoreLink,
              }}
            />
          </div>
        </div>
      </SettingsRow>

      {isDisablePending ? (
        <DisableLocalBackupsDialog
          i18n={i18n}
          disableLocalBackups={disableLocalBackups}
          onCancel={() => setIsDisablePending(false)}
          onComplete={() => {
            setIsDisablePending(false);
            setSettingsLocation({ page: SettingsPage.Backups });
          }}
          onError={() => {
            showToast({
              toastType: ToastType.Error,
            });
            setIsDisablePending(false);
            setSettingsLocation({ page: SettingsPage.Backups });
          }}
        />
      ) : null}

      {authError ? (
        <AxoAlertDialog.Root
          open
          onOpenChange={open => {
            if (!open) {
              setAuthError(undefined);
            }
          }}
        >
          <AxoAlertDialog.Content escape="cancel-is-noop">
            <AxoAlertDialog.Title screenReaderOnly>
              {i18n('icu:Toast--error')}
            </AxoAlertDialog.Title>
            <AxoAlertDialog.Body>
              <AxoAlertDialog.Description>
                {i18n(
                  'icu:Preferences__local-backups-auth-error--unauthorized'
                )}
              </AxoAlertDialog.Description>
            </AxoAlertDialog.Body>
            <AxoAlertDialog.Footer>
              <AxoAlertDialog.Cancel>{i18n('icu:ok')}</AxoAlertDialog.Cancel>
            </AxoAlertDialog.Footer>
          </AxoAlertDialog.Content>
        </AxoAlertDialog.Root>
      ) : null}

      {isShowingBackupKeyChangedModal ? (
        <AxoAlertDialog.Root
          open
          onOpenChange={open => {
            if (!open) {
              setIsShowingBackupKeyChangedModal(false);
            }
          }}
        >
          <div className={tw('p-4')}>
            <AxoAlertDialog.Content escape="cancel-is-noop">
              <AxoAlertDialog.Body>
                <div className={tw('my-3 flex flex-col items-center')}>
                  <LocalBackupSetupIcon symbol="key" />
                  <AxoAlertDialog.Title>
                    <div className={tw('mt-3 type-title-medium')}>
                      {i18n('icu:Preferences__recovery-key-updated__title')}
                    </div>
                  </AxoAlertDialog.Title>
                </div>
                <AxoAlertDialog.Description>
                  <div className={tw('mb-3')}>
                    {i18n('icu:Preferences__recovery-key-updated__description')}
                  </div>
                </AxoAlertDialog.Description>
              </AxoAlertDialog.Body>
              <AxoAlertDialog.Footer>
                <AxoAlertDialog.Cancel />
                <AxoAlertDialog.Action
                  variant="strong-primary"
                  onClick={showKeyReferenceWithAuth}
                >
                  {i18n('icu:Preferences__recovery-key-updated__view-key')}
                </AxoAlertDialog.Action>
              </AxoAlertDialog.Footer>
            </AxoAlertDialog.Content>
          </div>
        </AxoAlertDialog.Root>
      ) : null}
    </>
  );
}

function DisableLocalBackupsDialog({
  i18n,
  disableLocalBackups,
  onComplete,
  onCancel,
  onError,
}: {
  i18n: LocalizerType;
  disableLocalBackups: ({
    deleteExistingBackups,
  }: {
    deleteExistingBackups: boolean;
  }) => Promise<void>;
  onComplete: () => void;
  onCancel: () => void;
  onError: (e: unknown) => void;
}) {
  const [isPending, setIsPending] = useState<boolean>(false);

  const [deleteExistingBackups, setDeleteExistingBackups] =
    useState<boolean>(true);

  const handleDisableLocalBackups = useCallback(async () => {
    if (isPending) {
      return;
    }

    try {
      setIsPending(true);
      await disableLocalBackups({ deleteExistingBackups });
      onComplete();
    } catch (e) {
      log.error(
        'Error when disabling local backups',
        { deleteExistingBackups },
        toLogFormat(e)
      );
      onError(e);
    } finally {
      setIsPending(false);
    }
  }, [
    isPending,
    deleteExistingBackups,
    onComplete,
    onError,
    disableLocalBackups,
  ]);

  return (
    <AxoDialog.Root
      open
      onOpenChange={(open: boolean) => {
        if (isPending) {
          return;
        }

        if (!open) {
          onCancel();
        }
      }}
    >
      <AxoDialog.Content
        size="md"
        escape={isPending ? 'cancel-is-destructive' : 'cancel-is-noop'}
      >
        <div className={tw('p-2')}>
          <AxoDialog.Header>
            <AxoDialog.Title>
              {i18n('icu:Preferences__local-backups-turn-off')}
            </AxoDialog.Title>
          </AxoDialog.Header>
          <AxoDialog.Body padding="normal">
            <AxoDialog.Description>
              <div className={tw('mb-2 text-secondary')}>
                {i18n('icu:Preferences__local-backups-turn-off-confirmation')}
              </div>
            </AxoDialog.Description>

            <label
              className={tw('flex items-center gap-3 px-4 py-2.5')}
              htmlFor="deleteLocalBackupsCheckbox"
            >
              <AxoCheckbox.Root
                id="deleteLocalBackupsCheckbox"
                variant="square"
                checked={deleteExistingBackups}
                disabled={isPending}
                onCheckedChange={setDeleteExistingBackups}
              />
              {i18n('icu:Preferences__local-backups-turn-off-delete')}
            </label>
          </AxoDialog.Body>
          <AxoDialog.Footer>
            <AxoDialog.Actions>
              <AxoDialog.Action
                variant="strong-secondary"
                onClick={onCancel}
                disabled={isPending}
              >
                {i18n('icu:cancel')}
              </AxoDialog.Action>
              <AxoDialog.Action
                variant="strong-destructive"
                pending={isPending}
                onClick={handleDisableLocalBackups}
              >
                {i18n('icu:Preferences__local-backups-turn-off-action')}
              </AxoDialog.Action>
            </AxoDialog.Actions>
          </AxoDialog.Footer>
        </div>
      </AxoDialog.Content>
    </AxoDialog.Root>
  );
}

function LocalBackupsSetupFolderPicker({
  i18n,
  pickLocalBackupFolder,
}: {
  i18n: LocalizerType;
  pickLocalBackupFolder: () => Promise<string | undefined>;
}): JSX.Element {
  return (
    <div className="Preferences--LocalBackupsSetupScreen Preferences__padding">
      <div className="Preferences--LocalBackupsSetupScreenPaneContent">
        <div className="Preferences--LocalBackupsSetupIcon Preferences--LocalBackupsSetupIcon-folder" />
        <legend className="Preferences--LocalBackupsSetupScreenHeader">
          {i18n('icu:Preferences--local-backups-setup-folder')}
        </legend>
        <div className="Preferences--LocalBackupsSetupScreenBody Preferences--LocalBackupsSetupScreenBody--folder">
          {i18n('icu:Preferences--local-backups-setup-folder-description')}
        </div>
        <AxoButton.Root
          variant="strong-primary"
          size="lg"
          onClick={pickLocalBackupFolder}
        >
          {i18n('icu:Preferences__button--choose-folder')}
        </AxoButton.Root>
      </div>
    </div>
  );
}

type BackupKeyStep = 'view' | 'confirm' | 'caution' | 'reference';

function Strong(parts: Array<string | JSX.Element>) {
  return <strong>{parts}</strong>;
}

function LocalBackupsBackupKeyViewer({
  backupKey,
  i18n,
  isReferencing,
  onBackupKeyViewed,
  showToast,
}: {
  backupKey: string;
  i18n: LocalizerType;
  isReferencing: boolean;
  onBackupKeyViewed: () => void;
  showToast: ShowToastAction;
}): JSX.Element {
  const [isBackupKeyConfirmed, setIsBackupKeyConfirmed] =
    useState<boolean>(false);
  const [isShowingDoNotShareModal, setIsShowingDoNotShareModal] =
    useState<boolean>(false);
  const [step, setStep] = useState<BackupKeyStep>(
    isReferencing ? 'reference' : 'view'
  );
  const isStepViewOrReference = step === 'view' || step === 'reference';

  const backupKeyForDisplay = useMemo(
    () => formatBackupKeyForDisplay(backupKey, { convertAmbiguousChars: true }),
    [backupKey]
  );
  const onCopyBackupKey = useCallback(() => {
    window.SignalClipboard.copyTextTemporarily(
      backupKeyForDisplay,
      45 * SECOND
    );
    showToast({ toastType: ToastType.CopiedBackupKey });
  }, [backupKeyForDisplay, showToast]);

  const learnMorePhishingLink = (parts: Array<string | JSX.Element>) => (
    <a
      className={tw('whitespace-nowrap text-accent')}
      href={SIGNAL_USER_SAFETY_LINK}
      rel="noreferrer"
      target="_blank"
    >
      {parts}
    </a>
  );

  let title: string;
  let description: JSX.Element | string;
  let footerLeft: JSX.Element | undefined;
  let footerRight: JSX.Element;
  if (isStepViewOrReference) {
    title = i18n('icu:Preferences--local-backups-recovery-key--title');
    description = (
      <I18n
        id="icu:Preferences--local-backups-recovery-key--description"
        i18n={i18n}
        components={{
          learnMoreLink: learnMorePhishingLink,
        }}
      />
    );
    if (step === 'view') {
      footerRight = (
        <AxoButton.Root
          variant="strong-primary"
          size="lg"
          onClick={() => setStep('confirm')}
        >
          {i18n('icu:Preferences--local-backups-setup-next')}
        </AxoButton.Root>
      );
    } else {
      footerRight = (
        <AxoButton.Root
          variant="strong-primary"
          size="lg"
          onClick={onBackupKeyViewed}
        >
          {i18n('icu:Preferences--local-backups-view-backup-key-done')}
        </AxoButton.Root>
      );
    }
  } else {
    title = i18n('icu:Preferences--local-backups-confirm-recovery-key');
    description = i18n(
      'icu:Preferences--local-backups-confirm-recovery-key-description'
    );
    footerLeft = (
      <AxoButton.Root
        variant="implied-primary"
        size="lg"
        onClick={() => setStep('view')}
      >
        {i18n('icu:Preferences--local-backups-see-backup-key-again')}
      </AxoButton.Root>
    );
    footerRight = (
      <AxoButton.Root
        variant="strong-primary"
        size="lg"
        disabled={!isBackupKeyConfirmed}
        onClick={() => setStep('caution')}
      >
        {i18n('icu:Preferences--local-backups-setup-next')}
      </AxoButton.Root>
    );
  }

  return (
    <div className="Preferences--LocalBackupsSetupScreen Preferences__settings-pane-content--with-footer Preferences__padding">
      {isShowingDoNotShareModal ? (
        <AxoAlertDialog.Root
          open
          onOpenChange={() => setIsShowingDoNotShareModal(false)}
        >
          <AxoAlertDialog.Content escape="cancel-is-noop">
            <AxoAlertDialog.Body>
              <div className={tw('mt-3 mb-2 flex flex-col items-center')}>
                <img
                  role="presentation"
                  alt=""
                  className={tw('mt-1 mb-3 size-16 shrink-0')}
                  src="images/warning-circle.svg"
                />
                <AxoAlertDialog.Title>
                  {i18n('icu:Preferences__recovery-key__do-not-share-title')}
                </AxoAlertDialog.Title>
              </div>
              <AxoAlertDialog.Description>
                <div className={tw('mb-3 text-center type-body-medium')}>
                  <I18n
                    i18n={i18n}
                    id="icu:CompositionInput__recovery-key-warning--description"
                    components={{
                      strong: Strong,
                      learnMoreLink: learnMorePhishingLink,
                    }}
                  />
                </div>
              </AxoAlertDialog.Description>
            </AxoAlertDialog.Body>
            <AxoAlertDialog.Footer>
              <AxoAlertDialog.Action
                variant="strong-primary"
                onClick={() => {
                  onCopyBackupKey();
                  setIsShowingDoNotShareModal(false);
                }}
              >
                {i18n('icu:Preferences__recovery-key__do-not-share-confirm')}
              </AxoAlertDialog.Action>
            </AxoAlertDialog.Footer>
          </AxoAlertDialog.Content>
        </AxoAlertDialog.Root>
      ) : null}
      {step === 'caution' && (
        <Modal
          i18n={i18n}
          modalName="LocalBackupsConfirmKeyModal"
          moduleClassName="Preferences--LocalBackupsConfirmKeyModal"
          noMouseClose
          noEscapeClose
          modalFooter={
            <AxoButton.Root
              variant="strong-primary"
              size="lg"
              onClick={onBackupKeyViewed}
            >
              {i18n(
                'icu:Preferences__local-backups-confirm-key-modal-continue'
              )}
            </AxoButton.Root>
          }
          onClose={() => setStep('confirm')}
          padded={false}
        >
          <div className="Preferences--LocalBackupsSetupIcon Preferences--LocalBackupsSetupIcon-key" />
          <legend className="Preferences--LocalBackupsConfirmKeyModalTitle">
            {i18n('icu:Preferences__local-backups-confirm-key-modal-title')}
          </legend>
          <div className="Preferences--LocalBackupsConfirmKeyModalBody">
            {i18n('icu:Preferences__local-backups-confirm-key-modal-body')}
          </div>
        </Modal>
      )}
      <div className="Preferences--LocalBackupsSetupScreenPane Preferences--LocalBackupsSetupScreenPane-top">
        <div className="Preferences--LocalBackupsSetupScreenPaneContent">
          <div className="Preferences--LocalBackupsSetupIcon Preferences--LocalBackupsSetupIcon-lock" />
          <legend className="Preferences--LocalBackupsSetupScreenHeader">
            {title}
          </legend>
          <div className="Preferences--LocalBackupsSetupScreenBody">
            {description}
          </div>
        </div>
      </div>
      <div className="Preferences--LocalBackupsSetupScreenPane">
        <div className="Preferences--LocalBackupsSetupScreenPaneContent">
          <LocalBackupsBackupKeyTextarea
            key={step === 'view' ? 'view' : 'reference'}
            backupKeyForDisplay={backupKeyForDisplay}
            i18n={i18n}
            onValidate={(isValid: boolean) => setIsBackupKeyConfirmed(isValid)}
            isStepViewOrReference={isStepViewOrReference}
          />
        </div>
        {isStepViewOrReference && (
          <div className="Preferences--LocalBackupsSetupScreenPaneContent">
            <AxoButton.Root
              variant="strong-secondary"
              size="sm"
              symbol="copy"
              onClick={() => setIsShowingDoNotShareModal(true)}
            >
              {i18n('icu:Preferences__local-backups-copy-key')}
            </AxoButton.Root>
          </div>
        )}
      </div>
      <div className="Preferences--LocalBackupsSetupScreenPane Preferences--LocalBackupsSetupScreenPane-footer">
        <div className="Preferences--LocalBackupsSetupScreenFooterSection">
          {footerLeft}
        </div>
        <div className="Preferences--LocalBackupsSetupScreenFooterSection Preferences--LocalBackupsSetupScreenFooterSection-right">
          {footerRight}
        </div>
      </div>
    </div>
  );
}

function LocalBackupsBackupKeyTextarea({
  backupKeyForDisplay,
  i18n,
  onValidate,
  isStepViewOrReference,
}: {
  backupKeyForDisplay: string;
  i18n: LocalizerType;
  onValidate: (isValid: boolean) => void;
  isStepViewOrReference: boolean;
}): JSX.Element {
  const backupKeyTextareaRef = useRef<HTMLTextAreaElement>(null);
  const [backupKeyInput, setBackupKeyInput] = useState<string>('');

  const handleTextareaChange = useCallback(
    (ev: ChangeEvent<HTMLTextAreaElement>) => {
      const { selectionStart, value } = ev.target;

      setBackupKeyInput(
        formatBackupKeyForDisplay(value, { convertAmbiguousChars: false })
      );

      const currentCharIndex = value
        .slice(0, selectionStart)
        .replace(/\s/g, '').length;
      const newCaretIndex =
        currentCharIndex + Math.max(0, Math.floor((currentCharIndex - 1) / 4));

      requestAnimationFrame(() => {
        backupKeyTextareaRef.current?.setSelectionRange(
          newCaretIndex,
          newCaretIndex
        );
      });

      onValidate(
        formatBackupKeyForDisplay(value, { convertAmbiguousChars: true }) ===
          backupKeyForDisplay
      );
    },
    [backupKeyForDisplay, onValidate]
  );

  return (
    <textarea
      aria-label={i18n('icu:Preferences--local-backups-recovery-key-text-box')}
      autoFocus
      className="Preferences--LocalBackupsBackupKey"
      dir="ltr"
      maxLength={79}
      onChange={isStepViewOrReference ? noop : handleTextareaChange}
      placeholder={i18n('icu:Preferences--local-backups-enter-backup-key')}
      readOnly={isStepViewOrReference}
      ref={backupKeyTextareaRef}
      spellCheck="false"
      value={isStepViewOrReference ? backupKeyForDisplay : backupKeyInput}
    />
  );
}

function LocalBackupSetupIcon(props: { symbol: 'key' | 'lock' }): JSX.Element {
  return (
    <div
      className={tw(
        // eslint-disable-next-line better-tailwindcss/no-restricted-classes
        'inline-flex size-16 items-center justify-center rounded-full bg-[#D2DFFB] text-[#3B45FD]'
      )}
    >
      <AxoSymbol.Icon symbol={props.symbol} size={36} label={null} />
    </div>
  );
}
