import { useRef } from 'react';
import type {
  ModelId,
  ModelLoadProgress,
  StreamState,
} from '../../lib/contracts';
import { formatBytes, formatPercent } from '../../lib/format';

interface ComposerActivityProps {
  modelId: ModelId;
  streamState: StreamState;
  progress: ModelLoadProgress;
}

function ratio(value: number, total: number): number {
  return total > 0 ? Math.min(1, Math.max(0, value / total)) : 0;
}

function humanizeStage(value: string | null): string {
  if (!value) return '';
  return value.replaceAll('_', ' ').replace(/\b\w/g, (letter) => letter.toUpperCase());
}

export function overallLoadProgress(progress: ModelLoadProgress): number {
  const stageProgress = Math.min(1, Math.max(0, progress.stageProgress));
  const loadedShardBytes = progress.shards.reduce((total, shard) => (
    total + (shard.state === 'cached' || shard.state === 'complete'
      ? shard.totalBytes
      : Math.min(shard.loadedBytes, shard.totalBytes))
  ), 0);
  const verifiedBytes = progress.shards.reduce((total, shard) => (
    total + (shard.state === 'cached' || shard.state === 'complete'
      ? shard.totalBytes
      : Math.min(shard.verifiedBytes, shard.totalBytes))
  ), 0);
  const downloadRatio = ratio(Math.max(progress.downloadedBytes, loadedShardBytes), progress.totalBytes);
  const verifiedRatio = ratio(verifiedBytes, progress.totalBytes);
  const transferProgress = Math.min(0.9, 0.08 + downloadRatio * 0.57 + verifiedRatio * 0.25);

  switch (progress.stage) {
    case 'manifest':
      return stageProgress * 0.03;
    case 'cache':
      return Math.max(0.03 + stageProgress * 0.05, transferProgress);
    case 'download':
    case 'verify':
      return transferProgress;
    case 'load':
      return 0.9 + stageProgress * 0.1;
    case 'ready':
      return 1;
    default:
      return 0;
  }
}

export function describeLoadProgress(progress: ModelLoadProgress): { label: string; detail: string } {
  const shardCount = progress.shards.length;
  const readyShards = progress.shards.filter((shard) => shard.state === 'cached' || shard.state === 'complete').length;
  const activeShard = progress.shards.find((shard) => shard.state === 'downloading' || shard.state === 'verifying');
  const shardDetail = shardCount > 0 ? `${readyShards} of ${shardCount} shards ready` : 'Preparing shard map';
  const byteDetail = progress.totalBytes > 0
    ? `${formatBytes(progress.downloadedBytes)} / ${formatBytes(progress.totalBytes)}`
    : '';

  switch (progress.stage) {
    case 'manifest':
      return { label: 'Reading model manifest', detail: progress.modelLabel || 'Pinned release metadata' };
    case 'cache':
      return { label: 'Checking local cache', detail: shardCount > 0 ? shardDetail : 'Looking for existing shards' };
    case 'download':
      return {
        label: activeShard ? `Downloading shard ${activeShard.index + 1} of ${shardCount}` : 'Downloading model shards',
        detail: [shardDetail, byteDetail].filter(Boolean).join(' · '),
      };
    case 'verify':
      return {
        label: activeShard ? `Verifying shard ${activeShard.index + 1} of ${shardCount}` : 'Verifying model shards',
        detail: [shardDetail, byteDetail].filter(Boolean).join(' · '),
      };
    case 'load':
      return {
        label: progress.nativeStage ? humanizeStage(progress.nativeStage) : 'Allocating model',
        detail: progress.residentBytes > 0 ? `${formatBytes(progress.residentBytes)} resident` : 'Building the local graph',
      };
    default:
      return { label: 'Preparing model', detail: progress.modelLabel };
  }
}

export function ComposerActivity({ modelId, streamState, progress }: ComposerActivityProps) {
  const displayedProgress = useRef<{ modelId: ModelId | null; value: number }>({ modelId: null, value: 0 });
  const isLoading = streamState === 'loading' && progress.modelId === modelId;
  if (!isLoading) {
    displayedProgress.current = { modelId: null, value: 0 };
    return null;
  }

  const measuredProgress = overallLoadProgress(progress);
  const overallProgress = displayedProgress.current.modelId === modelId
    ? Math.max(displayedProgress.current.value, measuredProgress)
    : measuredProgress;
  displayedProgress.current = { modelId, value: overallProgress };
  const status = describeLoadProgress(progress);
  const percentage = Math.round(overallProgress * 100);

  return (
    <section className="composer-activity" aria-label="Model load progress">
      <div className="activity-status" aria-live="polite" aria-atomic="true">
        <div>
          <span className="activity-pulse" aria-hidden="true" />
          <span>
            <strong>{status.label}</strong>
            <small>{status.detail}</small>
          </span>
        </div>
        <b>{formatPercent(overallProgress)}</b>
      </div>
      <div
        className="activity-track"
        role="progressbar"
        aria-label={`${status.label}: ${status.detail}`}
        aria-valuemin={0}
        aria-valuemax={100}
        aria-valuenow={percentage}
      >
        <span style={{ width: `${percentage}%` }} />
      </div>
    </section>
  );
}
