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

import { useEffect, useState, type JSX } from 'react';
import classNames from 'classnames';

import type {
  BackupMediaDownloadStatusType,
  BackupsSubscriptionType,
  BackupStatusType,
} from '../types/backups.node.ts';
import type { LocalizerType } from '../types/I18N.std.ts';
import {
  SettingsControl as Control,
  FlowingSettingsControl as FlowingControl,
  LightIconLabel,
  SettingsRow,
} from './PreferencesUtil.dom.tsx';
import type { SettingsLocation } from '../types/Nav.std.ts';
import { SettingsPage } from '../types/Nav.std.ts';
import { I18n } from './I18n.dom.tsx';
import { PreferencesLocalBackups } from './PreferencesLocalBackups.dom.tsx';
import type { ShowToastAction } from '../state/ducks/toast.preload.ts';
import type {
  PromptOSAuthReasonType,
  PromptOSAuthResultType,
} from '../util/os/promptOSAuthMain.main.ts';
import { AxoButton } from '../axo/AxoButton.dom.tsx';
import { BackupLevel } from '../services/backups/types.std.ts';
import {
  BackupsDetailsPage,
  renderSubscriptionDetails,
} from './PreferencesBackupDetails.dom.tsx';
import type { LocalBackupExportMetadata } from '../types/LocalExport.std.ts';
import { createLogger } from '../logging/log.std.ts';
import { toLogFormat } from '../types/errors.std.ts';
import { AxoAlertDialog } from '../axo/AxoAlertDialog.dom.tsx';

export const SIGNAL_BACKUPS_LEARN_MORE_URL =
  'https://support.signal.org/hc/articles/360007059752-Backup-and-Restore-Messages';

const LOCAL_BACKUPS_PAGES = new Set([
  SettingsPage.LocalBackups,
  SettingsPage.LocalBackupsSetupKey,
  SettingsPage.LocalBackupsSetupFolder,
  SettingsPage.LocalBackupsKeyReference,
]);

function isLocalBackupsPage(page: SettingsPage) {
  return LOCAL_BACKUPS_PAGES.has(page);
}

const logger = createLogger('PreferencesBackups');

export function PreferencesBackups({
  backupKey,
  backupKeyHash,
  backupFreeMediaDays,
  backupSubscriptionStatus,
  backupTier,
  cloudBackupStatus,
  i18n,
  isLocalBackupsEnabled,
  lastLocalBackup,
  locale,
  localBackupFolder,
  onBackupKeyViewed,
  openFileInFolder,
  osName,
  pickLocalBackupFolder,
  disableLocalBackups,
  backupMediaDownloadStatus,
  cancelBackupMediaDownload,
  pauseBackupMediaDownload,
  resumeBackupMediaDownload,
  settingsLocation,
  previouslyViewedBackupKeyHash,
  promptOSAuth,
  refreshCloudBackupStatus,
  refreshBackupSubscriptionStatus,
  setSettingsLocation,
  showToast,
  startLocalBackupExport,
}: {
  backupFreeMediaDays: number;
  backupKey: string | undefined;
  backupKeyHash: string | undefined;
  backupSubscriptionStatus: BackupsSubscriptionType;
  backupTier: BackupLevel | null;
  cloudBackupStatus?: BackupStatusType;
  localBackupFolder: string | undefined;
  i18n: LocalizerType;
  isLocalBackupsEnabled: boolean;
  lastLocalBackup: LocalBackupExportMetadata | undefined;
  locale: string;
  onBackupKeyViewed: ({ backupKeyHash }: { backupKeyHash: string }) => void;
  openFileInFolder: (path: string) => void;
  osName: 'linux' | 'macos' | 'windows' | undefined;
  settingsLocation: SettingsLocation;
  backupMediaDownloadStatus: BackupMediaDownloadStatusType | undefined;
  cancelBackupMediaDownload: () => void;
  disableLocalBackups: ({
    deleteExistingBackups,
  }: {
    deleteExistingBackups: boolean;
  }) => Promise<void>;
  pauseBackupMediaDownload: () => void;
  resumeBackupMediaDownload: () => void;
  pickLocalBackupFolder: () => Promise<string | undefined>;
  previouslyViewedBackupKeyHash: string | undefined;
  promptOSAuth: (
    reason: PromptOSAuthReasonType
  ) => Promise<PromptOSAuthResultType>;
  refreshCloudBackupStatus: () => void;
  refreshBackupSubscriptionStatus: () => void;
  setSettingsLocation: (settingsLocation: SettingsLocation) => void;
  showToast: ShowToastAction;
  startLocalBackupExport: () => void;
}): JSX.Element | null {
  const [isAuthPending, setIsAuthPending] = useState<boolean>(false);
  const [authError, setAuthError] = useState<boolean>(false);

  useEffect(() => {
    if (settingsLocation.page === SettingsPage.Backups) {
      refreshBackupSubscriptionStatus();
    } else if (settingsLocation.page === SettingsPage.BackupsDetails) {
      refreshBackupSubscriptionStatus();
      refreshCloudBackupStatus();
    }
  }, [
    settingsLocation.page,
    refreshBackupSubscriptionStatus,
    refreshCloudBackupStatus,
  ]);

  if (!isLocalBackupsEnabled && isLocalBackupsPage(settingsLocation.page)) {
    setSettingsLocation({ page: SettingsPage.Backups });
    return null;
  }

  if (settingsLocation.page === SettingsPage.BackupsDetails) {
    if (backupTier == null) {
      setSettingsLocation({ page: SettingsPage.Backups });
      return null;
    }
    return (
      <BackupsDetailsPage
        i18n={i18n}
        cloudBackupStatus={cloudBackupStatus}
        backupTier={backupTier}
        backupFreeMediaDays={backupFreeMediaDays}
        backupSubscriptionStatus={backupSubscriptionStatus}
        backupMediaDownloadStatus={backupMediaDownloadStatus}
        cancelBackupMediaDownload={cancelBackupMediaDownload}
        pauseBackupMediaDownload={pauseBackupMediaDownload}
        resumeBackupMediaDownload={resumeBackupMediaDownload}
        locale={locale}
      />
    );
  }

  if (isLocalBackupsPage(settingsLocation.page)) {
    if (!backupKey || !backupKeyHash) {
      setSettingsLocation({ page: SettingsPage.Backups });
      return null;
    }

    return (
      <PreferencesLocalBackups
        backupKey={backupKey}
        backupKeyHash={backupKeyHash}
        i18n={i18n}
        lastLocalBackup={lastLocalBackup}
        localBackupFolder={localBackupFolder}
        onBackupKeyViewed={onBackupKeyViewed}
        openFileInFolder={openFileInFolder}
        osName={osName}
        settingsLocation={settingsLocation}
        pickLocalBackupFolder={pickLocalBackupFolder}
        disableLocalBackups={disableLocalBackups}
        previouslyViewedBackupKeyHash={previouslyViewedBackupKeyHash}
        promptOSAuth={promptOSAuth}
        setSettingsLocation={setSettingsLocation}
        showToast={showToast}
        startLocalBackupExport={startLocalBackupExport}
      />
    );
  }

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

  const isLocalBackupsSetup =
    localBackupFolder != null && previouslyViewedBackupKeyHash != null;

  function renderRemoteBackups() {
    return (
      <>
        {backupTier == null ? (
          <SettingsRow className="Preferences--BackupsRow">
            <Control
              icon="Preferences__BackupsIcon"
              left={
                <label>
                  {i18n('icu:Preferences--signal-backups')}{' '}
                  <div className="Preferences--backup-details__value">
                    <I18n
                      id="icu:Preferences--signal-backups-off-description"
                      i18n={i18n}
                      components={{
                        learnMoreLink,
                      }}
                    />
                  </div>
                </label>
              }
              right={null}
            />
          </SettingsRow>
        ) : (
          <SettingsRow className="Preferences--BackupsRow">
            <FlowingControl>
              <div className="Preferences__two-thirds-flow">
                <LightIconLabel icon="Preferences__BackupsIcon">
                  <label>
                    {i18n('icu:Preferences--signal-backups')}
                    <div className="Preferences__description">
                      {backupTier === BackupLevel.Paid
                        ? renderPaidBackupsSummary({
                            subscriptionStatus: backupSubscriptionStatus,
                            i18n,
                            locale,
                          })
                        : null}
                      {backupTier === BackupLevel.Free
                        ? renderFreeBackupsSummary({
                            i18n,
                            backupFreeMediaDays,
                          })
                        : null}
                    </div>
                  </label>
                </LightIconLabel>
              </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={() =>
                    setSettingsLocation({ page: SettingsPage.BackupsDetails })
                  }
                >
                  {i18n('icu:Preferences__button--manage')}
                </AxoButton.Root>
              </div>
            </FlowingControl>
          </SettingsRow>
        )}
      </>
    );
  }

  function renderLocalBackups() {
    return (
      <SettingsRow
        className="Preferences--BackupsRow"
        title={i18n('icu:Preferences__backup-other-ways')}
      >
        <FlowingControl>
          <div className="Preferences__two-thirds-flow">
            <LightIconLabel icon="Preferences__LocalBackupsIcon">
              <label>
                {i18n('icu:Preferences__local-backups')}{' '}
                <div className="Preferences__description">
                  {i18n('icu:Preferences--local-backups-off-description')}
                </div>
              </label>
            </LightIconLabel>
          </div>
          <div
            className={classNames(
              'Preferences__flow-button',
              'Preferences__one-third-flow',
              'Preferences__one-third-flow--align-right'
            )}
          >
            <AxoButton.Root
              variant="strong-secondary"
              size="lg"
              disabled={isAuthPending}
              onClick={async () => {
                if (isLocalBackupsSetup) {
                  setSettingsLocation({
                    page: SettingsPage.LocalBackups,
                  });
                } else {
                  try {
                    setIsAuthPending(true);
                    const result = await promptOSAuth('enable-backups');
                    if (result === 'success' || result === 'unsupported') {
                      setSettingsLocation({
                        page: SettingsPage.LocalBackupsSetupFolder,
                      });
                    }

                    if (result === 'error') {
                      logger.error(
                        'Error returned when requesting OS auth for enabling backups'
                      );
                      setAuthError(true);
                    }
                    // We don't show an error when result is unauthorized
                  } catch (e) {
                    logger.error(
                      'Error thrown when requesting OS auth for enabling backups',
                      toLogFormat(e)
                    );
                    setAuthError(true);
                  } finally {
                    setIsAuthPending(false);
                  }
                }
              }}
            >
              {isLocalBackupsSetup
                ? i18n('icu:Preferences__button--manage')
                : i18n('icu:Preferences__button--set-up')}
            </AxoButton.Root>
          </div>
        </FlowingControl>
      </SettingsRow>
    );
  }

  return (
    <>
      <div className="Preferences__padding">
        <div className="Preferences__description Preferences__description--medium">
          {i18n('icu:Preferences--backup-section-description')}
        </div>
      </div>
      {renderRemoteBackups()}
      {isLocalBackupsEnabled ? renderLocalBackups() : null}
      {authError ? (
        <AxoAlertDialog.Root
          open
          onOpenChange={open => {
            if (!open) {
              setAuthError(false);
            }
          }}
        >
          <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--unknown-error'
                )}
              </AxoAlertDialog.Description>
            </AxoAlertDialog.Body>
            <AxoAlertDialog.Footer>
              <AxoAlertDialog.Cancel>{i18n('icu:ok')}</AxoAlertDialog.Cancel>
            </AxoAlertDialog.Footer>
          </AxoAlertDialog.Content>
        </AxoAlertDialog.Root>
      ) : null}
    </>
  );
}

function renderPaidBackupsSummary({
  subscriptionStatus,
  i18n,
  locale,
}: {
  locale: string;
  subscriptionStatus: BackupsSubscriptionType;
  i18n: LocalizerType;
}): JSX.Element | null {
  return (
    <div className="Preferences--backups-summary__status-container">
      <div>
        <div className="Preferences--backups-summary__type">
          {i18n('icu:Preferences--backup-media-plan__description')}
        </div>
        <div className="Preferences--backups-summary__content">
          {renderSubscriptionDetails({ i18n, locale, subscriptionStatus })}
        </div>
      </div>
    </div>
  );
}

function renderFreeBackupsSummary({
  backupFreeMediaDays,
  i18n,
}: {
  backupFreeMediaDays: number;
  i18n: LocalizerType;
}): JSX.Element | null {
  return (
    <div className="Preferences--backups-summary__status-container">
      <div>
        <div className="Preferences--backups-summary__type">
          {i18n('icu:Preferences--backup-messages-plan__description', {
            mediaDayCount: backupFreeMediaDays,
          })}
        </div>
        <div className="Preferences--backups-summary__content">
          {i18n('icu:Preferences--backup-messages-plan__cost-description')}
        </div>
      </div>
    </div>
  );
}
