import {useCallback, useState, useEffect} from 'react';
import {
  DownloadPriority,
  fileHandler,
} from '@modules/chats/instances/FileHandler';
import {
  FileDownloadStatus,
  useAttachmentData,
} from '@modules/chats/store/zustand/useAttachments';

type State = {
  url: string;
  status: 'loading' | 'error' | 'success' | 'idle' | 'not_automatic_download';
};

interface Params {
  assetId?: number;
  messageId?: string;
  channel: string;
}

/**
 * Hook for audio + document attachments. (Videos are streamed by
 * expo-video directly; images are served by ExpoImage. Both bypass
 * FileHandler entirely.) Resolves the local cached URI when available
 * and drives FileHandler downloads otherwise.
 */
export const useChatFile = ({assetId, messageId, channel}: Params) => {
  const fileData = useAttachmentData(messageId, assetId);
  const [state, setState] = useState<State>(() => {
    if (!fileData) {
      return {url: '', status: 'idle'};
    }
    if (fileData.state === FileDownloadStatus.DONE && fileData.uri) {
      return {url: fileData.uri, status: 'success'};
    }
    if (fileData.state === FileDownloadStatus.ERROR) {
      return {url: '', status: 'error'};
    }
    if (
      fileData.state === FileDownloadStatus.PROGRESS ||
      fileData.state === FileDownloadStatus.BACKEND_NOT_READY
    ) {
      return {url: '', status: 'loading'};
    }
    if (fileData.state === FileDownloadStatus.NOT_AUTOMATIC_DOWNLOAD) {
      return {url: '', status: 'not_automatic_download'};
    }
    return {url: '', status: 'idle'};
  });
  // Tracks whether this hook instance has performed at least one retry
  // after an initial attempt. Renderers use it to decide whether to show
  // a spinner: first fetch is silent, but once we've had to retry (hit
  // the Download button) the user sees a spinner so the retry tap feels
  // responsive.
  const [hasRetried, setHasRetried] = useState(false);

  useEffect(() => {
    if (assetId) {
      fileHandler.subscribe(assetId);
    }
    return () => {
      if (assetId) {
        fileHandler.unsubscribe(assetId);
      }
    };
  }, [messageId, assetId]);

  const getValidUrl = useCallback(async () => {
    try {
      if (!messageId || !assetId) {
        return;
      }

      if (!fileData || (!fileData.uri && !fileData.remoteUrl)) {
        // Should never happen, but just in case
        throw new Error('File data is not defined');
      }

      if (fileData.state === FileDownloadStatus.ERROR) {
        throw new Error('File has error');
      }

      if (
        fileData.state === FileDownloadStatus.PROGRESS ||
        fileData.state === FileDownloadStatus.BACKEND_NOT_READY
      ) {
        setState(prev => ({...prev, status: 'loading'}));
        return;
      }
      if (fileData.state === FileDownloadStatus.NOT_AUTOMATIC_DOWNLOAD) {
        setState(prev => ({...prev, status: 'not_automatic_download'}));
        return;
      }
      const checkIfLocalFileExists =
        fileData.uri && fileData.state === FileDownloadStatus.DONE
          ? await fileHandler.checkIfFileExists(fileData.uri)
          : false;

      if (checkIfLocalFileExists) {
        // The file is already downloaded, so we can use the local url
        setState({
          url: fileData.uri!,
          status: 'success',
        });
        return fileData!.uri;
      }

      // Only the remote url is provided
      if (fileData.remoteUrl && fileData.asset) {
        // Show a loading state while downloading the file (preserve any
        // existing url so the previously shown image stays mounted; Fix
        // 1 in Image gates the spinner on !hasSource so no overlap).
        setState(prev => ({...prev, status: 'loading'}));

        const localUrl = await fileHandler.downloadRemoteFile(
          {
            assetId: fileData.assetId,
            url: fileData.remoteUrl,
            filename: fileData.filename,
            messageId,
            shouldDownload: true,
            asset: fileData.asset,
            channel,
          },
          DownloadPriority.ForegroundVisible,
        );

        // Update the state with the local url
        if (localUrl) {
          setState({
            url: localUrl,
            status: 'success',
          });
        } else {
          setState(prev => ({
            ...prev,
            status: 'error',
          }));
        }
      }
    } catch (error) {
      // If there is an error, set the state to error
      setState(prev => ({
        ...prev,
        status: 'error',
      }));
    }
  }, [messageId, assetId, fileData, channel]);

  const retryDownload = useCallback(async () => {
    try {
      if (!messageId || !assetId) {
        return;
      }

      if (!fileData || (!fileData.uri && !fileData.remoteUrl)) {
        // Should never happen, but just in case
        throw new Error('File data is not defined');
      }
      if (fileData.remoteUrl && fileData.asset) {
        // Retry is only reachable from the error overlay, so we can clear
        // url to make the spinner clearly visible.
        setState({
          url: '',
          status: 'loading',
        });
        // Latch "we have retried this asset at least once". Renderers use
        // this to switch from silent-first-fetch mode to spinner-visible
        // mode for the remainder of this hook instance.
        setHasRetried(true);

        // Download the file. User explicitly tapped retry → UserInitiated
        // so this jumps ahead of any prefetch / visible backlog.
        const localUrl = await fileHandler.downloadRemoteFile(
          {
            assetId: fileData.assetId,
            url: fileData.remoteUrl,
            filename: fileData.filename,
            messageId,
            shouldDownload: true,
            asset: fileData.asset,
            channel,
          },
          DownloadPriority.UserInitiated,
        );

        // Update the state with the local url
        if (localUrl) {
          setState({
            url: localUrl,
            status: 'success',
          });
        } else {
          setState(prev => ({
            ...prev,
            status: 'error',
          }));
        }
      }
    } catch (error) {
      // If there is an error, set the state to error
      setState(prev => ({
        ...prev,
        status: 'error',
      }));
    }
  }, [messageId, assetId, fileData, channel]);

  const markAsError = useCallback(() => {
    setState(prev => ({
      ...prev,
      status: 'error',
    }));
  }, []);

  useEffect(() => {
    getValidUrl();
  }, [getValidUrl]);

  // Safety sync: if Zustand says the file is DONE with a local uri but our
  // local state hasn't caught up (e.g. mounted before the store was populated,
  // or getValidUrl errored out of a throw branch earlier), force the hook
  // back into success so the image renders. Prevents the "empty tile even
  // though the file is on disk" state that a remount would otherwise fix.
  useEffect(() => {
    if (fileData?.state === FileDownloadStatus.DONE && fileData.uri) {
      const uri = fileData.uri;
      setState(prev => {
        if (prev.status === 'success' && prev.url === uri) {
          return prev;
        }
        return {url: uri, status: 'success'};
      });
    }
  }, [fileData?.state, fileData?.uri]);

  return {
    url: state.url,
    progress: fileData?.progress || 0,
    isLoading: state.status === 'loading',
    isError: state.status === 'error',
    isSuccess: state.status === 'success',
    isNotAutomaticDownload: state.status === 'not_automatic_download',
    hasRetried,
    retryDownload,
    markAsError,
    asset: fileData?.asset,
  };
};
