export as namespace WebRTCPlayerSDK;

export type PlayerStateValue =
  | "idle"
  | "creating_session"
  | "connecting_p2p"
  | "connecting_turn"
  | "playing"
  | "stopping"
  | "stopped"
  | "failed"
  | "destroyed";

export type MediaRouteValue = "none" | "p2p-direct" | "turn-relay";
export type VideoSource = "screen" | "camera";
export type AudioSource = "mic" | "system" | "mute";

export interface ICEServer {
  urls: string | string[];
  username?: string;
  credential?: string;
}

export interface PlayerOptions {
  baseUrl?: string;
  token?: string;
  tokenProvider?: () => string | Promise<string>;
  deviceId: string;
  videoElement?: HTMLVideoElement;
  source?: VideoSource;
  audioSource?: AudioSource;
  width?: number;
  height?: number;
  fps?: number;
  videoBitrateBps?: number;
  degradationPreference?: "MAINTAIN_RESOLUTION" | "MAINTAIN_FRAMERATE" | "BALANCED";
  iceServers?: ICEServer[];
  directTimeoutMs?: number;
  turnTimeoutMs?: number;
  fallbackDecisionTimeoutMs?: number;
  websocketOpenTimeoutMs?: number;
  startupPollIntervalMs?: number;
  connectedPollIntervalMs?: number;
  heartbeatIntervalMs?: number;
  sourceSwitchTimeoutMs?: number;
  disconnectedGraceMs?: number;
  muted?: boolean;
  autoplay?: boolean;
  autoStopOnPageHide?: boolean;
  debug?: boolean;
  fetch?: typeof fetch;
  WebSocket?: typeof WebSocket | null;
  RTCPeerConnection?: typeof RTCPeerConnection;
  RTCRtpReceiver?: typeof RTCRtpReceiver;
}

export interface DeviceListOptions {
  baseUrl?: string;
  token?: string;
  tokenProvider?: () => string | Promise<string>;
  fetch?: typeof fetch;
}

export interface WatchDevice {
  deviceId: string;
  displayName: string;
  online: boolean;
  state: string;
  currentStreamId: string;
  appVersion: string;
  authorized: boolean;
  lastSeenAt: string;
}

export interface DeviceListResult {
  totalCount: number;
  onlineCount: number;
  offlineCount: number;
  devices: WatchDevice[];
}

export interface PlayerDiagnostics {
  sdkVersion: string;
  state: PlayerStateValue;
  route: MediaRouteValue;
  deviceId: string;
  streamId: string;
  sessionId: string;
  viewerSessionId: string;
  pathAttemptId: string;
  attemptNo: number;
  source: VideoSource;
  audioSource: AudioSource;
  peerConnectionState: RTCPeerConnectionState | "closed";
  iceConnectionState: RTCIceConnectionState | "closed";
  hasTURN: boolean;
  startedAt: number;
  connectedAt: number;
  connectTimeMs: number;
  lastFallbackReason: string;
  lastErrorCode: string;
}

export interface PlayerStats {
  bytesReceived?: number;
  packetsReceived?: number;
  packetsLost?: number;
  framesDecoded?: number;
  framesPerSecond?: number;
  codec?: string;
  candidateType?: string;
}

export class PlayerError extends Error {
  readonly code: string;
  readonly details: Record<string, unknown>;
  readonly cause?: unknown;
}

export class WebRTCPlayer {
  constructor(options: PlayerOptions);
  readonly state: PlayerStateValue;
  readonly route: MediaRouteValue;
  on(event: string, listener: (payload: any) => void): () => void;
  once(event: string, listener: (payload: any) => void): () => void;
  off(event: string, listener: (payload: any) => void): void;
  start(videoElement?: HTMLVideoElement): Promise<PlayerDiagnostics>;
  stop(options?: { reason?: string; keepalive?: boolean }): Promise<void>;
  destroy(): Promise<void>;
  resume(): Promise<void>;
  switchSource(source: VideoSource): Promise<{ source: VideoSource; switchId?: string; unchanged?: boolean }>;
  getDiagnostics(): PlayerDiagnostics;
  getStats(): Promise<PlayerStats>;
}

export const VERSION: string;
export const PlayerState: Readonly<Record<string, PlayerStateValue>>;
export const MediaRoute: Readonly<Record<string, MediaRouteValue>>;
export const ErrorCode: Readonly<Record<string, string>>;
export function createPlayer(options: PlayerOptions): WebRTCPlayer;
export function listDevices(options?: DeviceListOptions): Promise<DeviceListResult>;
