import type { StreamMetadata, LaneInitSpec } from '@shade/streams';

/**
 * Browser File System Access API handle. Declared locally as an opaque
 * type so this package builds without `lib: ["DOM"]`. Consumers in browsers
 * pass a real `FileSystemFileHandle`; the runtime structural shape is
 * checked at use-sites.
 */
export interface FileSystemFileHandleLike {
  readonly kind: 'file';
  readonly name: string;
  createWritable(): Promise<unknown>;
}

/** Default per-chunk plaintext size (bytes). */
export const DEFAULT_CHUNK_SIZE = 1024 * 1024; // 1 MiB

/** Hard cap on per-chunk plaintext size. */
export const DEFAULT_MAX_CHUNK_SIZE = 16 * 1024 * 1024; // 16 MiB

/** Default lane count for parallel transfers. */
export const DEFAULT_LANE_COUNT = 4;

/**
 * Anything we accept as the byte source for an upload. The orchestrator
 * normalizes these (see `sender/input.ts`) into a chunk reader.
 */
export type TransferInput =
  | Uint8Array
  | Blob
  | File
  | ReadableStream<Uint8Array>;

/** Sink kinds for an incoming transfer. The receiver picks ONE. */
export type TransferOutput =
  | { kind: 'pipe'; pipeTo: WritableStream<Uint8Array> }
  | { kind: 'callback'; onChunk: (chunk: Uint8Array) => void | Promise<void> }
  | { kind: 'buffer' }
  | { kind: 'file'; path: string }
  | { kind: 'fileHandle'; handle: FileSystemFileHandleLike };

export type TransferDirection = 'send' | 'receive';
export type TransferStatus =
  | 'pending'
  | 'active'
  | 'paused'
  | 'finished'
  | 'aborted'
  | 'failed';

export interface LaneProgress {
  laneId: number;
  bytesSent: number;
  bytesTotal?: number;
  seq: number;
  state: 'idle' | 'sending' | 'paused' | 'done' | 'error';
}

export interface TransferProgress {
  streamId: string;
  bytesSent: number;
  bytesTotal?: number;
  /** EMA-smoothed throughput in bytes/sec. */
  bytesPerSecond: number;
  etaSeconds?: number;
  /** [0, 1] when bytesTotal is known; undefined otherwise. */
  percent?: number;
  lanes: LaneProgress[];
}

export type TransferEvent =
  | { type: 'start'; streamId: string }
  | { type: 'progress'; progress: TransferProgress }
  | { type: 'lane-done'; laneId: number }
  | { type: 'pause'; reason: 'network' | 'manual' }
  | { type: 'resume' }
  | { type: 'complete'; streamId: string; sha256: string; durationMs: number }
  | { type: 'abort'; reason: string }
  | { type: 'error'; error: unknown };

export interface TransferResult {
  streamId: string;
  bytesSent: number;
  /** Hex-encoded sha256 over the original plaintext. */
  sha256: string;
  durationMs: number;
}

export interface TransferOptions {
  to: string;
  input: TransferInput;
  metadata?: Partial<StreamMetadata>;
  /** Lane count override; capped at 64 internally. */
  lanes?: number;
  chunkSize?: number;
  maxChunkSize?: number;
  partition?: 'auto' | 'range' | 'round-robin';
  signal?: AbortSignal;
  onProgress?: (p: TransferProgress) => void;
  onEvent?: (e: TransferEvent) => void;
}

export interface TransferHandle {
  readonly streamId: string;
  readonly events: AsyncIterable<TransferEvent>;
  pause(): Promise<void>;
  resume(): Promise<void>;
  abort(reason?: string): Promise<void>;
  done(): Promise<TransferResult>;
}

export interface IncomingTransferAcceptOptions {
  output: TransferOutput;
  onProgress?: (p: TransferProgress) => void;
  onEvent?: (e: TransferEvent) => void;
}

export interface IncomingTransfer {
  readonly streamId: string;
  readonly from: string;
  readonly metadata: StreamMetadata;
  readonly lanes: LaneInitSpec[];
  /** Accept and start receiving. Throws if already accepted/declined. */
  accept(options: IncomingTransferAcceptOptions): Promise<TransferHandle>;
  /** Reject; sends abort to sender. */
  decline(reason?: string): Promise<void>;
}

export interface TransferSummary {
  streamId: string;
  direction: TransferDirection;
  peerAddress: string;
  status: TransferStatus;
  bytesTotal?: number;
  bytesProcessed: number;
  createdAt: number;
  updatedAt: number;
  metadata: StreamMetadata | null;
}
