// Screen-share quality presets modelled on Discord's tiers. Values are the
// upper bounds — LiveKit + WebRTC's congestion control dynamically drop to
// lower spatial/temporal layers (SVC with VP9) when the uplink degrades, so
// these numbers behave as "up to" caps, not constant bitrates.

const STORAGE_KEY = 'chatapp.screenshare';

export type ScreenSharePreset =
  | 'auto'
  | '720p30'
  | '720p60'
  | '1080p30'
  | '1080p60'
  | '1440p60'
  | '4k60';

// Browser getDisplayMedia `displaySurface` hint. The OS picker honours this
// to pre-filter the source list (monitor = whole display, window = single
// window). `null` leaves everything selectable.
export type DisplaySurfaceHint = 'monitor' | 'window' | null;

export interface ScreenShareSettings {
  preset: ScreenSharePreset;
  displaySurface: DisplaySurfaceHint;
  // User-chosen framerate. `null` falls back to preset's default.
  framerateOverride: number | null;
  // Include system audio ("go live" style). On some hosts getDisplayMedia
  // can't capture system audio (macOS without special entitlements, some
  // Linux setups). If the browser ignores the `audio: true` request we
  // silently fall through to a video-only share.
  includeSystemAudio: boolean;
  // While system audio is being shared, mute the local playback of remote
  // call audio to prevent peers from hearing themselves echoed back via the
  // loopback capture. Defaults to true — Chromium's loopback grant in
  // Electron *should* exclude the app's own render output, but the
  // process-tree exclusion isn't always watertight (WebView2/Chromium audio
  // sessions can render in process owners outside the tree). The user can
  // opt out (e.g. when they route call audio to a separate output device
  // with setSinkId, in which case the default-render-endpoint capture
  // never sees it).
  duckRemoteAudioWhileSharing: boolean;
}

const DEFAULTS: ScreenShareSettings = {
  preset: 'auto',
  displaySurface: null,
  framerateOverride: null,
  includeSystemAudio: false,
  // Default off — we use Electron's `loopback` (not `loopbackWithMute`)
  // so the user keeps local audio while sharing. Auto-ducking remote
  // mic audio for echo prevention also kills the user's ability to
  // hear peers, which most users don't want. Opt-in only.
  duckRemoteAudioWhileSharing: false,
};

export interface PresetParams {
  dims: { width: number; height: number } | null; // null = browser picks native
  framerate: number;
  bitrateKbps: number;
  label: string;
}

const PRESET_PARAMS: Record<ScreenSharePreset, PresetParams> = {
  auto: { dims: null, framerate: 30, bitrateKbps: 8000, label: 'Auto (Original)' },
  '720p30': {
    dims: { width: 1280, height: 720 },
    framerate: 30,
    bitrateKbps: 2500,
    label: '720p · 30 fps',
  },
  '720p60': {
    dims: { width: 1280, height: 720 },
    framerate: 60,
    bitrateKbps: 3500,
    label: '720p · 60 fps',
  },
  '1080p30': {
    dims: { width: 1920, height: 1080 },
    framerate: 30,
    bitrateKbps: 4000,
    label: '1080p · 30 fps',
  },
  '1080p60': {
    dims: { width: 1920, height: 1080 },
    framerate: 60,
    bitrateKbps: 6000,
    label: '1080p · 60 fps',
  },
  '1440p60': {
    dims: { width: 2560, height: 1440 },
    framerate: 60,
    bitrateKbps: 8000,
    label: '1440p · 60 fps',
  },
  '4k60': {
    dims: { width: 3840, height: 2160 },
    framerate: 60,
    bitrateKbps: 10_000,
    label: '4K · 60 fps',
  },
};

export const PRESET_ORDER: ReadonlyArray<ScreenSharePreset> = [
  'auto',
  '720p30',
  '720p60',
  '1080p30',
  '1080p60',
  '1440p60',
  '4k60',
];

export function getPresetParams(p: ScreenSharePreset): PresetParams {
  return PRESET_PARAMS[p];
}

type Listener = (s: ScreenShareSettings) => void;
const listeners = new Set<Listener>();

let cached: ScreenShareSettings | null = null;

function isPreset(v: unknown): v is ScreenSharePreset {
  return typeof v === 'string' && v in PRESET_PARAMS;
}

function read(): ScreenShareSettings {
  if (cached) return cached;
  try {
    const raw = window.localStorage.getItem(STORAGE_KEY);
    if (!raw) {
      cached = DEFAULTS;
      return cached;
    }
    const parsed = JSON.parse(raw) as Partial<ScreenShareSettings>;
    cached = {
      preset: isPreset(parsed.preset) ? parsed.preset : DEFAULTS.preset,
      displaySurface:
        parsed.displaySurface === 'monitor' || parsed.displaySurface === 'window'
          ? parsed.displaySurface
          : DEFAULTS.displaySurface,
      framerateOverride:
        typeof parsed.framerateOverride === 'number' && parsed.framerateOverride > 0
          ? parsed.framerateOverride
          : DEFAULTS.framerateOverride,
      includeSystemAudio:
        typeof parsed.includeSystemAudio === 'boolean'
          ? parsed.includeSystemAudio
          : DEFAULTS.includeSystemAudio,
      duckRemoteAudioWhileSharing:
        typeof parsed.duckRemoteAudioWhileSharing === 'boolean'
          ? parsed.duckRemoteAudioWhileSharing
          : DEFAULTS.duckRemoteAudioWhileSharing,
    };
    return cached;
  } catch {
    cached = DEFAULTS;
    return cached;
  }
}

function write(s: ScreenShareSettings): void {
  cached = s;
  try {
    window.localStorage.setItem(STORAGE_KEY, JSON.stringify(s));
  } catch {
    /* quota / private mode */
  }
  for (const l of listeners) l(s);
}

export function getScreenShareSettings(): ScreenShareSettings {
  return read();
}

export function updateScreenShareSettings(
  patch: Partial<ScreenShareSettings>,
): ScreenShareSettings {
  const next = { ...read(), ...patch };
  write(next);
  return next;
}

export function subscribeScreenShareSettings(listener: Listener): () => void {
  listeners.add(listener);
  return () => listeners.delete(listener);
}
