import { useCallback, useEffect, useRef, useState } from 'react';
import type { BulkTransferEvent, BulkTransferHandle, BulkTransferResult } from '../client/directory-types.js';

export interface BulkTransferStatus {
  state: 'idle' | 'running' | 'done' | 'error' | 'aborted';
  filesDone: number;
  filesTotal: number;
  bytesDone: number;
  bytesTotal: number;
  error: unknown;
  result: BulkTransferResult | null;
  /** Last event emitted by the underlying handle. */
  lastEvent: BulkTransferEvent | null;
}

export interface UseFileTransferResult extends BulkTransferStatus {
  start(handle: BulkTransferHandle): void;
  abort(reason?: string): Promise<void>;
}

const INITIAL: BulkTransferStatus = {
  state: 'idle',
  filesDone: 0,
  filesTotal: 0,
  bytesDone: 0,
  bytesTotal: 0,
  error: null,
  result: null,
  lastEvent: null,
};

/**
 * Generic React-state wrapper around a `BulkTransferHandle`. Apps wire the
 * handle returned from `uploadDirectory()` / `downloadDirectory()` here to
 * surface progress in their UI.
 *
 * `useFileUpload` and `useFileDownload` are thin presets — they call
 * `start(...)` with the appropriate handle automatically.
 */
export function useFileTransfer(): UseFileTransferResult {
  const [status, setStatus] = useState<BulkTransferStatus>(INITIAL);
  const handleRef = useRef<BulkTransferHandle | null>(null);

  const start = useCallback((handle: BulkTransferHandle): void => {
    handleRef.current = handle;
    setStatus({ ...INITIAL, state: 'running' });
    void (async () => {
      try {
        for await (const ev of handle.events) {
          setStatus((prev) => {
            const next: BulkTransferStatus = { ...prev, lastEvent: ev };
            if (ev.type === 'plan') {
              next.filesTotal = ev.totalFiles;
              next.bytesTotal = ev.totalBytes ?? 0;
            } else if (ev.type === 'progress') {
              next.filesDone = ev.filesDone;
              next.bytesDone = ev.bytesDone;
              next.bytesTotal = ev.bytesTotal ?? prev.bytesTotal;
              next.filesTotal = ev.filesTotal;
            } else if (ev.type === 'complete') {
              next.state = 'done';
              next.filesDone = ev.filesDone;
              next.bytesDone = ev.bytesDone;
            } else if (ev.type === 'abort') {
              next.state = 'aborted';
              next.error = new Error(ev.reason);
            } else if (ev.type === 'file-error') {
              next.error = ev.error;
            }
            return next;
          });
        }
        const result = await handle.done();
        setStatus((prev) => ({ ...prev, state: 'done', result }));
      } catch (err) {
        setStatus((prev) => ({ ...prev, state: 'error', error: err }));
      }
    })();
  }, []);

  const abort = useCallback(async (reason?: string): Promise<void> => {
    if (handleRef.current === null) return;
    await handleRef.current.abort(reason);
  }, []);

  // Cleanup on unmount: abort any in-flight transfer.
  useEffect(() => {
    return () => {
      if (handleRef.current !== null) {
        void handleRef.current.abort('unmount').catch(() => undefined);
      }
    };
  }, []);

  return { ...status, start, abort };
}

/**
 * Preset: `useFileUpload` — semantically identical to `useFileTransfer`,
 * named distinctly to mirror the `useFileDownload` pair.
 */
export const useFileUpload = useFileTransfer;
export const useFileDownload = useFileTransfer;
