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

import React, { useRef, useState } from 'react';
import classNames from 'classnames';

import { SpinnerV2 } from '../SpinnerV2';
import { usePrevious } from '../../hooks/usePrevious';

import type { AttachmentForUIType } from '../../types/Attachment';

const TRANSITION_DELAY = 200;

export type PropsType = {
  attachment: AttachmentForUIType | undefined;
  isAttachmentNotAvailable: boolean;
  isIncoming: boolean;
  renderAttachmentDownloaded: () => JSX.Element;
};

enum IconState {
  NeedsDownload = 'NeedsDownload',
  Downloading = 'Downloading',
  Downloaded = 'Downloaded',
}

export function AttachmentStatusIcon({
  attachment,
  isAttachmentNotAvailable,
  isIncoming,
  renderAttachmentDownloaded,
}: PropsType): JSX.Element | null {
  const [isWaiting, setIsWaiting] = useState<boolean>(false);

  let state: IconState = IconState.Downloaded;
  if (attachment && isAttachmentNotAvailable) {
    state = IconState.Downloaded;
  } else if (attachment && !attachment.path && !attachment.pending) {
    state = IconState.NeedsDownload;
  } else if (attachment && !attachment.path && attachment.pending) {
    state = IconState.Downloading;
  }

  const timerRef = useRef<NodeJS.Timeout | undefined>();
  const previousState = usePrevious(state, state);

  // We need useLayoutEffect; otherwise we might get a flash of the wrong visual state.
  // We do calculations here which change the UI!
  React.useLayoutEffect(() => {
    if (state === previousState) {
      return;
    }

    if (
      previousState === IconState.NeedsDownload &&
      state === IconState.Downloading
    ) {
      setIsWaiting(true);
      if (timerRef.current) {
        clearTimeout(timerRef.current);
      }
      timerRef.current = setTimeout(() => {
        timerRef.current = undefined;
        setIsWaiting(false);
      }, TRANSITION_DELAY);
    } else if (
      previousState === IconState.Downloading &&
      state === IconState.Downloaded
    ) {
      setIsWaiting(true);
      if (timerRef.current) {
        clearTimeout(timerRef.current);
      }
      timerRef.current = setTimeout(() => {
        timerRef.current = undefined;
        setIsWaiting(false);
      }, TRANSITION_DELAY);
    }
  }, [previousState, state]);

  if (attachment && state === IconState.NeedsDownload) {
    return (
      <div className="AttachmentStatusIcon__container">
        <div
          className={classNames(
            'AttachmentStatusIcon__circle-icon-container',
            isIncoming
              ? 'AttachmentStatusIcon__circle-icon-container--incoming'
              : undefined
          )}
        >
          <div
            className={classNames(
              'AttachmentStatusIcon__circle-icon',
              isIncoming
                ? 'AttachmentStatusIcon__circle-icon--incoming'
                : undefined,
              'AttachmentStatusIcon__circle-icon--arrow-down'
            )}
          />
        </div>
      </div>
    );
  }

  if (
    attachment &&
    (state === IconState.Downloading ||
      (state === IconState.Downloaded && isWaiting))
  ) {
    const { size, totalDownloaded } = attachment;
    let spinnerValue = (size && totalDownloaded) || undefined;
    if (state === IconState.Downloading && isWaiting) {
      spinnerValue = undefined;
    }
    if (state === IconState.Downloaded && isWaiting) {
      spinnerValue = size;
    }

    return (
      <div className="AttachmentStatusIcon__container">
        <div
          className={classNames(
            'AttachmentStatusIcon__circle-icon-container',
            isIncoming
              ? 'AttachmentStatusIcon__circle-icon-container--incoming'
              : undefined
          )}
        >
          <div
            className={classNames(
              'AttachmentStatusIcon__progress-container',
              isIncoming
                ? 'AttachmentStatusIcon__progress-container--incoming'
                : undefined
            )}
          >
            <SpinnerV2
              min={0}
              max={size}
              value={spinnerValue}
              variant={isIncoming ? 'no-background-incoming' : 'no-background'}
              size={36}
              strokeWidth={2}
              marginRatio={1}
            />
          </div>
          <div
            className={classNames(
              'AttachmentStatusIcon__circle-icon',
              isIncoming
                ? 'AttachmentStatusIcon__circle-icon--incoming'
                : undefined,
              'AttachmentStatusIcon__circle-icon--x'
            )}
          />
        </div>
      </div>
    );
  }

  return (
    <div className="AttachmentStatusIcon__container">
      {renderAttachmentDownloaded()}
    </div>
  );
}
