import React from 'react';
import type {
  IncomingTransfer,
  TransferHandle,
  TransferOutput,
  TransferProgress,
} from '@shade/sdk';
import { useShadeRuntimeTheme } from '../../ShadeRuntimeProvider.js';
import {
  useShadeDownload,
  type DownloadEntry,
  type UseShadeDownloadOptions,
} from '../../useShadeDownload.js';
import { TransferRow } from './TransferRow.js';

export interface ShadeDownloaderProps {
  /** Auto-accept incoming transfers via this callback (returns the output sink). */
  autoAccept?: UseShadeDownloadOptions['autoAccept'];
  /** Render the prompt for a pending (not-yet-accepted) transfer. */
  renderPending?: (args: {
    incoming: IncomingTransfer;
    accept: (output: TransferOutput) => Promise<TransferHandle>;
    decline: (reason?: string) => Promise<void>;
  }) => React.ReactNode;
  /** Render-prop for active transfer rows. */
  renderRow?: (args: {
    handle: TransferHandle;
    progress: TransferProgress | null;
    name: string;
    done: boolean;
    error: unknown;
    onDismiss: () => void;
  }) => React.ReactNode;
  /** Render-prop for the empty state (no pending or active transfers). */
  renderEmpty?: () => React.ReactNode;
  className?: string;
  style?: React.CSSProperties;
}

/**
 * Composite downloader: lists pending (awaiting accept) and active
 * (in-progress) transfers, with bundled defaults for both. Customize via
 * `renderPending` / `renderRow`.
 */
export function ShadeDownloader({
  autoAccept,
  renderPending,
  renderRow,
  renderEmpty,
  className,
  style,
}: ShadeDownloaderProps): React.ReactElement {
  const theme = useShadeRuntimeTheme();
  const { pending, active, accept, decline, dismiss } = useShadeDownload(
    autoAccept !== undefined ? { autoAccept } : {},
  );

  return (
    <div
      className={className}
      style={{
        display: 'flex',
        flexDirection: 'column',
        gap: 12,
        fontFamily: theme.font,
        color: theme.text,
        ...style,
      }}
    >
      {pending.length === 0 && active.length === 0 ? (
        renderEmpty?.() ?? <span style={{ color: theme.textDim }}>Ingen overføringer</span>
      ) : null}

      {pending.map((entry) => {
        const acceptForOutput = (output: TransferOutput): Promise<TransferHandle> =>
          accept(entry.incoming.streamId, output);
        const declineWith = (reason?: string): Promise<void> =>
          decline(entry.incoming.streamId, reason);
        if (renderPending !== undefined) {
          return (
            <React.Fragment key={entry.incoming.streamId}>
              {renderPending({ incoming: entry.incoming, accept: acceptForOutput, decline: declineWith })}
            </React.Fragment>
          );
        }
        return (
          <DefaultPendingRow
            key={entry.incoming.streamId}
            incoming={entry.incoming}
            onAccept={acceptForOutput}
            onDecline={() => void declineWith('user-decline')}
          />
        );
      })}

      {active.map((entry: DownloadEntry) => {
        if (entry.handle === null) return null;
        const name = entry.incoming.metadata.name ?? entry.incoming.streamId.slice(0, 8);
        const dismissEntry = (): void => dismiss(entry.incoming.streamId);
        const handle = entry.handle;
        if (renderRow !== undefined) {
          return (
            <React.Fragment key={entry.incoming.streamId}>
              {renderRow({
                handle,
                progress: entry.progress,
                name,
                done: entry.done,
                error: entry.error,
                onDismiss: dismissEntry,
              })}
            </React.Fragment>
          );
        }
        return (
          <TransferRow
            key={entry.incoming.streamId}
            handle={handle}
            progress={entry.progress}
            name={name}
            done={entry.done}
            error={entry.error}
            {...(entry.incoming.metadata.sizeBytes !== undefined
              ? { bytesTotal: entry.incoming.metadata.sizeBytes }
              : {})}
            onDismiss={dismissEntry}
          />
        );
      })}
    </div>
  );
}

function DefaultPendingRow({
  incoming,
  onAccept,
  onDecline,
}: {
  incoming: IncomingTransfer;
  onAccept: (output: TransferOutput) => Promise<TransferHandle>;
  onDecline: () => void;
}): React.ReactElement {
  const theme = useShadeRuntimeTheme();
  return (
    <div
      style={{
        padding: 12,
        background: theme.bgElevated,
        border: `1px solid ${theme.border}`,
        borderRadius: theme.radius,
        display: 'flex',
        flexDirection: 'column',
        gap: 8,
      }}
    >
      <div style={{ fontSize: 13 }}>
        Innkommende overføring fra <strong>{incoming.from}</strong>
      </div>
      <div style={{ fontSize: 12, color: theme.textMuted }}>
        {incoming.metadata.name ?? incoming.streamId}
        {incoming.metadata.sizeBytes !== undefined
          ? ` · ${formatBytes(incoming.metadata.sizeBytes)}`
          : ''}
      </div>
      <div style={{ display: 'flex', gap: 8 }}>
        <button
          type="button"
          onClick={() => void onAccept({ kind: 'buffer' })}
          style={{
            padding: '4px 10px',
            background: theme.accent,
            color: theme.bg,
            border: 'none',
            borderRadius: 4,
            fontSize: 12,
            cursor: 'pointer',
          }}
        >
          Godta (i minne)
        </button>
        <button
          type="button"
          onClick={onDecline}
          style={{
            padding: '4px 10px',
            background: theme.bg,
            color: theme.textMuted,
            border: `1px solid ${theme.border}`,
            borderRadius: 4,
            fontSize: 12,
            cursor: 'pointer',
          }}
        >
          Avslå
        </button>
      </div>
    </div>
  );
}

function formatBytes(b: number): string {
  if (b < 1024) return `${b} B`;
  if (b < 1024 * 1024) return `${(b / 1024).toFixed(1)} KB`;
  if (b < 1024 * 1024 * 1024) return `${(b / (1024 * 1024)).toFixed(1)} MB`;
  return `${(b / (1024 * 1024 * 1024)).toFixed(1)} GB`;
}
