<!-- TRACES: UR-023, UR-025, UR-027, UR-029, UR-057 | DR-030, DR-048, DR-077, DR-086, DR-132 -->
<script lang="ts">
  import { onDestroy, onMount } from "svelte";
  import { commands } from "$lib/api/bindings";
  import type {
    AudioSettings,
    CacheConfig,
    EqPreset,
    VideoSettings,
    VolumeLevel,
  } from "$lib/api/bindings";
  import {
    getCacheStats,
    setCacheLimit,
    clearCache,
    formatBytes,
    gbToBytes,
    bytesToGb,
    type ImageCacheStats,
  } from "$lib/services/imageCache";
  import { getCacheConfig, updateCacheConfig } from "$lib/services/preload";
  import SearchGroupOrderList from "$lib/components/settings/SearchGroupOrderList.svelte";
  import PendingSyncList from "$lib/components/sync/PendingSyncList.svelte";
  import { library, viewMode } from "$lib/stores/library";
  import {
    isNetworkDetectionSupported,
    reportNetworkState,
  } from "$lib/services/networkType";
  import { experimentalNativeVideo } from "$lib/stores/nativeVideo";
  import { getPlaybackCapabilities } from "$lib/services/playbackCapabilities";

  const episodeLimitOptions = [
    { value: 0, label: "Unlimited" },
    { value: 1, label: "1" },
    { value: 2, label: "2" },
    { value: 3, label: "3" },
    { value: 5, label: "5" },
    { value: 10, label: "10" },
  ];

  let settings = $state<AudioSettings>({
    crossfadeDuration: 0,
    gaplessPlayback: true,
    normalizeVolume: false,
    volumeLevel: "normal",
    equalizerEnabled: false,
    equalizerBands: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  });

  // Equalizer band centre-frequency labels (must match Rust EQ_BANDS order).
  // Presentation only — the gain curves themselves come from the backend.
  const EQ_BAND_LABELS = ["31", "62", "125", "250", "500", "1k", "2k", "4k", "8k", "16k"];
  const EQ_GAIN_MIN = -12;
  const EQ_GAIN_MAX = 12;
  // Preset name → gain curve, fetched from the backend (domain data lives in Rust).
  let eqPresets = $state<[EqPreset, number[]][]>([]);
  // Non-optional view of the bands for template bindings (the wire type marks
  // equalizerBands optional via serde default; loadSettings guarantees it dense).
  const eqBands = $derived(settings.equalizerBands ?? [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

  let videoSettings = $state<VideoSettings>({
    autoPlayNextEpisode: true,
    autoPlayCountdownSeconds: 10,
    autoPlayMaxEpisodes: 0,
  });

  // Download/caching behaviour, incl. the WiFi-only gate (UR-053).
  let cacheConfig = $state<CacheConfig>({
    queuePrecacheEnabled: true,
    queuePrecacheCount: 3,
    albumAffinityEnabled: true,
    albumAffinityThreshold: 3,
    storageLimit: 10 * 1024 * 1024 * 1024,
    wifiOnly: false,
    // Placeholder only — replaced by the backend's value on load. How long a
    // temporary (auto-cached) download lives before it is reclaimed; the policy
    // itself is Rust's (DR-127).
    temporaryTtlHours: 24 * 7,
  });

  // Whether the platform can actually detect the network type. On desktop it
  // can't, so the WiFi-only toggle would be inert — we disable and explain it
  // rather than offering a switch that does nothing.
  let networkDetectionSupported = $state(false);

  let loading = $state(true);

  // Image cache state
  let cacheStats = $state<ImageCacheStats | null>(null);
  let cacheLoading = $state(false);
  let clearingCache = $state(false);

  // Cache limit options in bytes
  const cacheLimitOptions = [
    { label: "500 MB", bytes: gbToBytes(0.5) },
    { label: "1 GB", bytes: gbToBytes(1), default: true },
    { label: "2 GB", bytes: gbToBytes(2) },
    { label: "5 GB", bytes: gbToBytes(5) },
    { label: "Unlimited", bytes: 0 },
  ];

  // Native-video opt-in (Android). `supportsNativeVideo` comes from Rust, which
  // owns the "does this platform have a native video surface" decision; the
  // toggle is hidden entirely where it cannot apply.
  let supportsNativeVideo = $state(false);
  let nativeVideoEnabled = $state(false);

  const unsubscribeNativeVideo = experimentalNativeVideo.subscribe((v) => {
    nativeVideoEnabled = v;
  });

  function handleNativeVideoToggle() {
    experimentalNativeVideo.set(!nativeVideoEnabled);
  }

  // Not returned from onMount: that callback is async, so its return value is a
  // Promise and Svelte would never invoke it as a teardown.
  onDestroy(unsubscribeNativeVideo);

  onMount(async () => {
    await loadSettings();
    supportsNativeVideo = (await getPlaybackCapabilities()).supportsNativeVideo;
  });

  async function loadSettings() {
    try {
      loading = true;
      networkDetectionSupported = isNetworkDetectionSupported();
      const [audioResult, videoResult, cacheResult, presets] = await Promise.all([
        commands.playerGetAudioSettings(),
        commands.playerGetVideoSettings(),
        getCacheConfig(),
        commands.playerGetEqPresets(),
      ]);
      // equalizerBands is optional on the wire (serde default); guarantee a
      // dense 10-band array so the slider bindings are never undefined.
      settings = {
        ...audioResult,
        equalizerBands: audioResult.equalizerBands ?? [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
      };
      videoSettings = videoResult;
      cacheConfig = cacheResult;
      eqPresets = presets;
      // Load cache stats in parallel but don't block on it
      loadCacheStats();
    } catch (e) {
      console.error("Failed to load settings:", e);
    } finally {
      loading = false;
    }
  }

  async function loadCacheStats() {
    try {
      cacheLoading = true;
      cacheStats = await getCacheStats();
    } catch (e) {
      console.error("Failed to load cache stats:", e);
    } finally {
      cacheLoading = false;
    }
  }

  async function handleCacheLimitChange(limitBytes: number) {
    try {
      await setCacheLimit(limitBytes);
      // Reload stats to reflect new limit
      await loadCacheStats();
    } catch (e) {
      console.error("Failed to set cache limit:", e);
    }
  }

  async function handleClearCache() {
    try {
      clearingCache = true;
      await clearCache();
      await loadCacheStats();
    } catch (e) {
      console.error("Failed to clear cache:", e);
    } finally {
      clearingCache = false;
    }
  }

  // Get the current selected limit option
  function isCurrentLimit(optionBytes: number): boolean {
    if (!cacheStats) return false;
    // Unlimited is 0
    if (optionBytes === 0 && cacheStats.limitBytes === 0) return true;
    // Allow small tolerance for floating point
    return Math.abs(cacheStats.limitBytes - optionBytes) < 1000;
  }

  // Calculate cache usage percentage
  function getCacheUsagePercent(): number {
    if (!cacheStats || cacheStats.limitBytes === 0) return 0;
    return Math.min(100, (cacheStats.totalSizeBytes / cacheStats.limitBytes) * 100);
  }

  // Settings apply the moment the user changes a control — there is no Save
  // button. Each helper writes just the settings group it owns so a single
  // toggle doesn't re-push unrelated state.
  async function persistAudio() {
    try {
      await commands.playerSetAudioSettings(settings);
    } catch (e) {
      console.error("Failed to save audio settings:", e);
    }
  }

  async function persistVideo() {
    try {
      await commands.playerSetVideoSettings(videoSettings);
    } catch (e) {
      console.error("Failed to save video settings:", e);
    }
  }

  async function persistCache() {
    try {
      await updateCacheConfig(cacheConfig);
      // Re-report the network so the backend re-evaluates the gate against the
      // just-changed wifi-only preference, releasing or holding the queue now
      // rather than at the next network change.
      await reportNetworkState();
    } catch (e) {
      console.error("Failed to save download settings:", e);
    }
  }

  // Slider drags fire `input` on every tick; update the live display there but
  // only persist on `change` (pointer release) so we don't spam the backend.
  function handleCrossfadeInput(e: Event) {
    const target = e.target as HTMLInputElement;
    settings.crossfadeDuration = parseFloat(target.value);
  }

  function handleCrossfadeChange(e: Event) {
    const target = e.target as HTMLInputElement;
    settings.crossfadeDuration = parseFloat(target.value);
    persistAudio();
  }

  function handleGaplessToggle() {
    settings.gaplessPlayback = !settings.gaplessPlayback;
    persistAudio();
  }

  function handleNormalizeToggle() {
    settings.normalizeVolume = !settings.normalizeVolume;
    persistAudio();
  }

  function handleVolumeLevelChange(level: VolumeLevel) {
    settings.volumeLevel = level;
    persistAudio();
  }

  // --- Equalizer (UR-027) ---

  function handleEqToggle() {
    settings.equalizerEnabled = !settings.equalizerEnabled;
    persistAudio();
  }

  // Apply a preset's gain curve (from the backend) to the bands.
  function handleEqPreset(gains: number[]) {
    settings.equalizerBands = [...gains];
    persistAudio();
  }

  // Live-update a single band while dragging; persist on release (change).
  function handleEqBandInput(index: number, e: Event) {
    const target = e.target as HTMLInputElement;
    const bands = [...eqBands];
    bands[index] = parseFloat(target.value);
    settings.equalizerBands = bands;
  }

  function handleEqBandChange(index: number, e: Event) {
    const target = e.target as HTMLInputElement;
    const bands = [...eqBands];
    bands[index] = parseFloat(target.value);
    settings.equalizerBands = bands;
    persistAudio();
  }

  // The name of the preset whose curve matches the current bands, or null
  // ("Custom"). Presentation-only label — the backend defines the curves.
  const activeEqPreset = $derived.by<EqPreset | null>(() => {
    const eq = eqBands;
    for (const [name, gains] of eqPresets) {
      if (gains.length === eq.length && gains.every((g, i) => g === eq[i])) {
        return name;
      }
    }
    return null;
  });

  // Human labels for preset chips.
  const EQ_PRESET_LABELS: Record<EqPreset, string> = {
    flat: "Flat",
    rock: "Rock",
    pop: "Pop",
    jazz: "Jazz",
    classical: "Classical",
    bassBoost: "Bass Boost",
    trebleBoost: "Treble Boost",
    vocal: "Vocal",
  };

  function handleAutoPlayToggle() {
    videoSettings.autoPlayNextEpisode = !videoSettings.autoPlayNextEpisode;
    persistVideo();
  }

  function handleCountdownInput(e: Event) {
    const target = e.target as HTMLInputElement;
    videoSettings.autoPlayCountdownSeconds = parseInt(target.value, 10);
  }

  function handleCountdownChange(e: Event) {
    const target = e.target as HTMLInputElement;
    videoSettings.autoPlayCountdownSeconds = parseInt(target.value, 10);
    persistVideo();
  }

  function handleEpisodeLimitChange(value: number) {
    videoSettings.autoPlayMaxEpisodes = value;
    persistVideo();
  }

  function handleSmartCachingToggle() {
    cacheConfig.albumAffinityEnabled = !cacheConfig.albumAffinityEnabled;
    persistCache();
  }

  function handleQueuePrecacheToggle() {
    cacheConfig.queuePrecacheEnabled = !cacheConfig.queuePrecacheEnabled;
    persistCache();
  }

  function handleWifiOnlyToggle() {
    cacheConfig.wifiOnly = !cacheConfig.wifiOnly;
    persistCache();
  }
</script>

<div class="max-w-2xl mx-auto space-y-8 p-6">
  <div>
    <h1 class="text-3xl font-bold text-white mb-2">Settings</h1>
    <p class="text-gray-400">Configure display, playback, and downloads</p>
  </div>

  {#if loading}
    <div class="text-center py-12 text-gray-400">
      <p>Loading settings...</p>
    </div>
  {:else}
    <div class="space-y-6">
      <!-- Display — grid/list preference, a second view onto the library
           viewMode store (same source of truth as the library page-header
           toggle, so the two stay in sync for free). -->
      <div id="display" class="scroll-mt-4 bg-[var(--color-surface)] rounded-lg p-6">
        <div class="mb-4">
          <h2 class="text-xl font-semibold text-white">Display</h2>
          <p class="text-sm text-gray-400 mt-1">
            How your library and collections are laid out
          </p>
        </div>
        <p class="text-sm font-medium text-gray-300 mb-3">Layout</p>
        <div class="grid grid-cols-2 gap-3">
          <button
            onclick={() => library.setViewMode("grid")}
            class="flex items-center justify-center gap-2 py-3 px-4 rounded-lg transition-all {$viewMode ===
            'grid'
              ? 'bg-[var(--color-jellyfin)] text-white'
              : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
            aria-pressed={$viewMode === "grid"}
          >
            <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
              <path d="M4 4h7v7H4V4zm9 0h7v7h-7V4zM4 13h7v7H4v-7zm9 0h7v7h-7v-7z" />
            </svg>
            <span class="font-semibold">Grid</span>
          </button>
          <button
            onclick={() => library.setViewMode("list")}
            class="flex items-center justify-center gap-2 py-3 px-4 rounded-lg transition-all {$viewMode ===
            'list'
              ? 'bg-[var(--color-jellyfin)] text-white'
              : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
            aria-pressed={$viewMode === "list"}
          >
            <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
              <path d="M3 5h18v2H3V5zm0 6h18v2H3v-2zm0 6h18v2H3v-2z" />
            </svg>
            <span class="font-semibold">List</span>
          </button>
        </div>
      </div>

      <!-- Crossfade -->
      <div class="bg-[var(--color-surface)] rounded-lg p-6">
        <div class="flex items-start justify-between mb-4">
          <div>
            <h2 class="text-xl font-semibold text-white">Crossfade</h2>
            <p class="text-sm text-gray-400 mt-1">
              Fade between tracks for seamless transitions
            </p>
          </div>
          <div class="text-right">
            <span class="text-2xl font-bold text-[var(--color-jellyfin)]">
              {settings.crossfadeDuration.toFixed(1)}s
            </span>
          </div>
        </div>
        <input
          type="range"
          min="0"
          max="12"
          step="0.5"
          value={settings.crossfadeDuration}
          oninput={handleCrossfadeInput}
          onchange={handleCrossfadeChange}
          class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-[var(--color-jellyfin)]"
        />
        <div class="flex justify-between text-xs text-gray-500 mt-2">
          <span>0s (Off)</span>
          <span>12s (Max)</span>
        </div>
      </div>

      <!-- Gapless Playback -->
      <div class="bg-[var(--color-surface)] rounded-lg p-6">
        <div class="flex items-center justify-between">
          <div>
            <h2 class="text-xl font-semibold text-white">Gapless Playback</h2>
            <p class="text-sm text-gray-400 mt-1">
              Eliminate silence between tracks in albums
            </p>
          </div>
          <button
            onclick={handleGaplessToggle}
            class="relative inline-flex h-8 w-14 items-center rounded-full transition-colors {settings.gaplessPlayback
              ? 'bg-[var(--color-jellyfin)]'
              : 'bg-gray-600'}"
            aria-label="Toggle gapless playback"
          >
            <span
              class="inline-block h-6 w-6 transform rounded-full bg-white transition-transform {settings.gaplessPlayback
                ? 'translate-x-7'
                : 'translate-x-1'}"
            ></span>
          </button>
        </div>
      </div>

      <!-- Volume Normalization -->
      <div class="bg-[var(--color-surface)] rounded-lg p-6 space-y-4">
        <div class="flex items-center justify-between">
          <div>
            <h2 class="text-xl font-semibold text-white">Volume Normalization</h2>
            <p class="text-sm text-gray-400 mt-1">
              Automatically adjust volume levels for consistent playback
            </p>
          </div>
          <button
            onclick={handleNormalizeToggle}
            class="relative inline-flex h-8 w-14 items-center rounded-full transition-colors {settings.normalizeVolume
              ? 'bg-[var(--color-jellyfin)]'
              : 'bg-gray-600'}"
            aria-label="Toggle volume normalization"
          >
            <span
              class="inline-block h-6 w-6 transform rounded-full bg-white transition-transform {settings.normalizeVolume
                ? 'translate-x-7'
                : 'translate-x-1'}"
            ></span>
          </button>
        </div>

        {#if settings.normalizeVolume}
          <div class="pt-4 border-t border-gray-700">
            <p class="text-sm font-medium text-gray-300 mb-3">Target Volume Level</p>
            <div class="grid grid-cols-3 gap-3">
              <button
                onclick={() => handleVolumeLevelChange("loud")}
                class="py-3 px-4 rounded-lg transition-all {settings.volumeLevel ===
                'loud'
                  ? 'bg-[var(--color-jellyfin)] text-white'
                  : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
              >
                <div class="font-semibold">Loud</div>
                <div class="text-xs opacity-75">-11 LUFS</div>
              </button>
              <button
                onclick={() => handleVolumeLevelChange("normal")}
                class="py-3 px-4 rounded-lg transition-all {settings.volumeLevel ===
                'normal'
                  ? 'bg-[var(--color-jellyfin)] text-white'
                  : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
              >
                <div class="font-semibold">Normal</div>
                <div class="text-xs opacity-75">-14 LUFS</div>
              </button>
              <button
                onclick={() => handleVolumeLevelChange("quiet")}
                class="py-3 px-4 rounded-lg transition-all {settings.volumeLevel ===
                'quiet'
                  ? 'bg-[var(--color-jellyfin)] text-white'
                  : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
              >
                <div class="font-semibold">Quiet</div>
                <div class="text-xs opacity-75">-23 LUFS</div>
              </button>
            </div>
          </div>
        {/if}
      </div>

      <!-- Equalizer (UR-027) -->
      <div class="bg-[var(--color-surface)] rounded-lg p-6 space-y-4">
        <div class="flex items-center justify-between">
          <div>
            <h2 class="text-xl font-semibold text-white">Equalizer</h2>
            <p class="text-sm text-gray-400 mt-1">
              Shape the sound with presets or custom bands (Linux)
            </p>
          </div>
          <button
            onclick={handleEqToggle}
            class="relative inline-flex h-8 w-14 items-center rounded-full transition-colors {settings.equalizerEnabled
              ? 'bg-[var(--color-jellyfin)]'
              : 'bg-gray-600'}"
            aria-label="Toggle equalizer"
          >
            <span
              class="inline-block h-6 w-6 transform rounded-full bg-white transition-transform {settings.equalizerEnabled
                ? 'translate-x-7'
                : 'translate-x-1'}"
            ></span>
          </button>
        </div>

        {#if settings.equalizerEnabled}
          <!-- Preset chips -->
          <div class="pt-4 border-t border-gray-700">
            <p class="text-sm font-medium text-gray-300 mb-3">Presets</p>
            <div class="flex flex-wrap gap-2">
              {#each eqPresets as [name, gains] (name)}
                <button
                  onclick={() => handleEqPreset(gains)}
                  class="px-3 py-1.5 rounded-full text-sm transition-all {activeEqPreset ===
                  name
                    ? 'bg-[var(--color-jellyfin)] text-white'
                    : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
                >
                  {EQ_PRESET_LABELS[name]}
                </button>
              {/each}
              {#if activeEqPreset === null}
                <span
                  class="px-3 py-1.5 rounded-full text-sm bg-[var(--color-jellyfin)] text-white"
                >
                  Custom
                </span>
              {/if}
            </div>
          </div>

          <!-- Band sliders -->
          <div class="pt-4 border-t border-gray-700">
            <p class="text-sm font-medium text-gray-300 mb-4">Bands (dB)</p>
            <div class="flex justify-between gap-1 sm:gap-2">
              {#each EQ_BAND_LABELS as label, i (label)}
                <div class="flex flex-col items-center gap-2 flex-1 min-w-0">
                  <span class="text-xs text-gray-400 tabular-nums">
                    {eqBands[i] > 0 ? "+" : ""}{eqBands[i]}
                  </span>
                  <input
                    type="range"
                    min={EQ_GAIN_MIN}
                    max={EQ_GAIN_MAX}
                    step="1"
                    value={eqBands[i]}
                    oninput={(e) => handleEqBandInput(i, e)}
                    onchange={(e) => handleEqBandChange(i, e)}
                    class="eq-slider"
                    aria-label="{label} Hz gain"
                  />
                  <span class="text-xs text-gray-500">{label}</span>
                </div>
              {/each}
            </div>
          </div>
        {/if}
      </div>

      <!-- Video Playback Settings -->
      <div class="border-t border-gray-700 pt-6">
        <h2 class="text-2xl font-bold text-white mb-4">Video Playback</h2>

        <!-- Auto-play Next Episode -->
        <div class="bg-[var(--color-surface)] rounded-lg p-6 space-y-4">
          <div class="flex items-center justify-between">
            <div>
              <h3 class="text-xl font-semibold text-white">Auto-play Next Episode</h3>
              <p class="text-sm text-gray-400 mt-1">
                Automatically start the next episode when one finishes
              </p>
            </div>
            <button
              onclick={handleAutoPlayToggle}
              class="relative inline-flex h-8 w-14 items-center rounded-full transition-colors {videoSettings.autoPlayNextEpisode
                ? 'bg-[var(--color-jellyfin)]'
                : 'bg-gray-600'}"
              aria-label="Toggle auto-play next episode"
            >
              <span
                class="inline-block h-6 w-6 transform rounded-full bg-white transition-transform {videoSettings.autoPlayNextEpisode
                  ? 'translate-x-7'
                  : 'translate-x-1'}"
              ></span>
            </button>
          </div>

          {#if videoSettings.autoPlayNextEpisode}
            <div class="pt-4 border-t border-gray-700">
              <div class="flex items-start justify-between mb-4">
                <div>
                  <p class="text-sm font-medium text-gray-300">Countdown Duration</p>
                  <p class="text-xs text-gray-500 mt-1">
                    Time before next episode starts automatically
                  </p>
                </div>
                <div class="text-right">
                  <span class="text-2xl font-bold text-[var(--color-jellyfin)]">
                    {videoSettings.autoPlayCountdownSeconds}s
                  </span>
                </div>
              </div>
              <input
                type="range"
                min="5"
                max="30"
                step="5"
                value={videoSettings.autoPlayCountdownSeconds}
                oninput={handleCountdownInput}
                onchange={handleCountdownChange}
                class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-[var(--color-jellyfin)]"
              />
              <div class="flex justify-between text-xs text-gray-500 mt-2">
                <span>5s</span>
                <span>30s</span>
              </div>
            </div>

            <!-- Episode Limit -->
            <div class="pt-4 border-t border-gray-700">
              <div class="mb-4">
                <p class="text-sm font-medium text-gray-300">Episode Limit</p>
                <p class="text-xs text-gray-500 mt-1">
                  Stop auto-playing after this many consecutive episodes
                </p>
              </div>
              <div class="grid grid-cols-3 md:grid-cols-6 gap-2">
                {#each episodeLimitOptions as option}
                  <button
                    onclick={() => handleEpisodeLimitChange(option.value)}
                    class="py-3 px-3 rounded-lg transition-all text-sm
                      {videoSettings.autoPlayMaxEpisodes === option.value
                        ? 'bg-[var(--color-jellyfin)] text-white'
                        : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
                  >
                    <div class="font-semibold">{option.label}</div>
                  </button>
                {/each}
              </div>
            </div>
          {/if}
        </div>

        <!-- Native video (experimental). Only rendered where the platform's Rust
             backend actually has a native video surface (Android). -->
        {#if supportsNativeVideo}
          <div class="bg-[var(--color-surface)] rounded-lg p-6 mt-4">
            <div class="flex items-center justify-between">
              <div class="pr-4">
                <h3 class="text-xl font-semibold text-white">
                  Native Video
                  <span
                    class="ml-2 align-middle text-xs font-medium uppercase tracking-wide text-amber-400 border border-amber-400/40 rounded px-1.5 py-0.5"
                  >
                    Experimental
                  </span>
                </h3>
                <p class="text-sm text-gray-400 mt-1">
                  Decode video with the device's hardware decoder instead of the
                  built-in web player. Better performance and battery life, but
                  less tested — turn this off if video fails to appear.
                </p>
              </div>
              <button
                onclick={handleNativeVideoToggle}
                class="relative inline-flex h-8 w-14 shrink-0 items-center rounded-full transition-colors {nativeVideoEnabled
                  ? 'bg-[var(--color-jellyfin)]'
                  : 'bg-gray-600'}"
                aria-label="Toggle native video"
              >
                <span
                  class="inline-block h-6 w-6 transform rounded-full bg-white transition-transform {nativeVideoEnabled
                    ? 'translate-x-7'
                    : 'translate-x-1'}"
                ></span>
              </button>
            </div>
            <p class="text-xs text-gray-500 mt-3">
              Takes effect the next time you start a video.
            </p>
          </div>
        {/if}
      </div>

      <!-- Search Settings -->
      <div class="border-t border-gray-700 pt-6">
        <h2 class="text-2xl font-bold text-white mb-4">Search</h2>

        <div class="bg-[var(--color-surface)] rounded-lg p-6">
          <h3 class="text-lg font-semibold text-white mb-1">Result Group Order</h3>
          <p class="text-sm text-gray-400 mb-4">
            Drag or use the arrows to choose the order search result groups appear in.
            Empty groups are hidden automatically.
          </p>
          <SearchGroupOrderList />
        </div>
      </div>

      <!-- Pending server updates. Lives here as well as behind the offline
           banner's badge, because a row that keeps failing is still queued once
           the server is reachable again — when no banner is on screen.
           TRACES: UR-025 | DR-132 -->
      <div class="border-t border-gray-700 pt-6">
        <h2 class="text-2xl font-bold text-white mb-4">Waiting to sync</h2>

        <div class="bg-[var(--color-surface)] rounded-lg p-6">
          <PendingSyncList />
        </div>
      </div>

      <!-- Image Cache Settings -->
      <div class="border-t border-gray-700 pt-6">
        <h2 class="text-2xl font-bold text-white mb-4">Image Cache</h2>

        <div class="bg-[var(--color-surface)] rounded-lg p-6 space-y-6">
          <!-- Cache Usage -->
          <div>
            <div class="flex items-center justify-between mb-2">
              <h3 class="text-lg font-semibold text-white">Cache Usage</h3>
              {#if cacheLoading}
                <span class="text-sm text-gray-400">Loading...</span>
              {:else if cacheStats}
                <span class="text-sm text-gray-300">
                  {formatBytes(cacheStats.totalSizeBytes)} / {cacheStats.limitBytes === 0 ? "Unlimited" : formatBytes(cacheStats.limitBytes)}
                </span>
              {/if}
            </div>

            {#if cacheStats && cacheStats.limitBytes > 0}
              <!-- Progress bar -->
              <div class="w-full bg-gray-700 rounded-full h-3 mb-2">
                <div
                  class="h-3 rounded-full transition-all duration-300 {getCacheUsagePercent() > 90 ? 'bg-red-500' : getCacheUsagePercent() > 70 ? 'bg-yellow-500' : 'bg-[var(--color-jellyfin)]'}"
                  style="width: {getCacheUsagePercent()}%"
                ></div>
              </div>
            {/if}

            <p class="text-sm text-gray-400">
              {#if cacheStats}
                {cacheStats.itemCount} images cached
              {:else}
                Thumbnails and artwork are cached locally for faster loading
              {/if}
            </p>
          </div>

          <!-- Cache Limit -->
          <div class="pt-4 border-t border-gray-700">
            <h3 class="text-lg font-semibold text-white mb-3">Storage Limit</h3>
            <div class="grid grid-cols-2 md:grid-cols-5 gap-2">
              {#each cacheLimitOptions as option}
                <button
                  onclick={() => handleCacheLimitChange(option.bytes)}
                  class="py-2 px-3 rounded-lg transition-all text-sm {isCurrentLimit(option.bytes)
                    ? 'bg-[var(--color-jellyfin)] text-white'
                    : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
                >
                  <div class="font-semibold">{option.label}</div>
                  {#if option.default}
                    <div class="text-xs opacity-75">Default</div>
                  {/if}
                </button>
              {/each}
            </div>
          </div>

          <!-- Clear Cache -->
          <div class="pt-4 border-t border-gray-700">
            <div class="flex items-center justify-between">
              <div>
                <h3 class="text-lg font-semibold text-white">Clear Image Cache</h3>
                <p class="text-sm text-gray-400">Remove all cached thumbnails and artwork</p>
              </div>
              <button
                onclick={handleClearCache}
                disabled={clearingCache || (cacheStats?.itemCount ?? 0) === 0}
                class="px-4 py-2 bg-red-600 text-white rounded-lg font-medium hover:bg-red-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
              >
                {clearingCache ? "Clearing..." : "Clear Cache"}
              </button>
            </div>
          </div>
        </div>
      </div>

      <!-- Download Settings -->
      <div class="border-t border-gray-700 pt-6">
        <h2 class="text-2xl font-bold text-white mb-4">Downloads</h2>

        <!-- Storage Limit -->
        <div class="bg-[var(--color-surface)] rounded-lg p-6 mb-4">
          <div class="mb-4">
            <h3 class="text-xl font-semibold text-white">Storage Limit</h3>
            <p class="text-sm text-gray-400 mt-1">
              Maximum storage for offline downloads
            </p>
          </div>
          <div class="grid grid-cols-2 md:grid-cols-4 gap-3">
            <button
              class="py-3 px-4 rounded-lg transition-all bg-gray-700 text-gray-300 hover:bg-gray-600"
            >
              <div class="font-semibold">5 GB</div>
            </button>
            <button
              class="py-3 px-4 rounded-lg transition-all bg-[var(--color-jellyfin)] text-white"
            >
              <div class="font-semibold">10 GB</div>
              <div class="text-xs opacity-75">Default</div>
            </button>
            <button
              class="py-3 px-4 rounded-lg transition-all bg-gray-700 text-gray-300 hover:bg-gray-600"
            >
              <div class="font-semibold">20 GB</div>
            </button>
            <button
              class="py-3 px-4 rounded-lg transition-all bg-gray-700 text-gray-300 hover:bg-gray-600"
            >
              <div class="font-semibold">Unlimited</div>
            </button>
          </div>
        </div>

        <!-- Smart Caching -->
        <div class="bg-[var(--color-surface)] rounded-lg p-6 mb-4">
          <div class="flex items-center justify-between">
            <div>
              <h3 class="text-xl font-semibold text-white">Smart Caching</h3>
              <p class="text-sm text-gray-400 mt-1">
                Automatically download albums you're listening to
              </p>
            </div>
            <button
              onclick={handleSmartCachingToggle}
              class="relative inline-flex h-8 w-14 items-center rounded-full transition-colors {cacheConfig.albumAffinityEnabled
                ? 'bg-[var(--color-jellyfin)]'
                : 'bg-gray-600'}"
              aria-label="Toggle smart caching"
              aria-pressed={cacheConfig.albumAffinityEnabled}
            >
              <span
                class="inline-block h-6 w-6 transform rounded-full bg-white transition-transform {cacheConfig.albumAffinityEnabled
                  ? 'translate-x-7'
                  : 'translate-x-1'}"
              ></span>
            </button>
          </div>
        </div>

        <!-- Queue Pre-caching -->
        <div class="bg-[var(--color-surface)] rounded-lg p-6 mb-4">
          <div class="flex items-center justify-between">
            <div>
              <h3 class="text-xl font-semibold text-white">Queue Pre-caching</h3>
              <p class="text-sm text-gray-400 mt-1">
                Download the next {cacheConfig.queuePrecacheCount} tracks in the queue
                automatically
              </p>
            </div>
            <button
              onclick={handleQueuePrecacheToggle}
              class="relative inline-flex h-8 w-14 items-center rounded-full transition-colors {cacheConfig.queuePrecacheEnabled
                ? 'bg-[var(--color-jellyfin)]'
                : 'bg-gray-600'}"
              aria-label="Toggle queue pre-caching"
              aria-pressed={cacheConfig.queuePrecacheEnabled}
            >
              <span
                class="inline-block h-6 w-6 transform rounded-full bg-white transition-transform {cacheConfig.queuePrecacheEnabled
                  ? 'translate-x-7'
                  : 'translate-x-1'}"
              ></span>
            </button>
          </div>
        </div>

        <!-- WiFi Only -->
        <div class="bg-[var(--color-surface)] rounded-lg p-6">
          <div class="flex items-center justify-between">
            <div>
              <h3 class="text-xl font-semibold text-white">WiFi Only</h3>
              <p class="text-sm text-gray-400 mt-1">
                {#if networkDetectionSupported}
                  Hold downloads unless on an unmetered network. Cellular and
                  metered hotspots are excluded; WiFi and Ethernet are allowed.
                {:else}
                  Only available on Android — this device has no metered
                  connection to detect.
                {/if}
              </p>
            </div>
            <button
              onclick={handleWifiOnlyToggle}
              class="relative inline-flex h-8 w-14 items-center rounded-full transition-colors {cacheConfig.wifiOnly
                ? 'bg-[var(--color-jellyfin)]'
                : 'bg-gray-600'} {networkDetectionSupported
                ? ''
                : 'opacity-50 cursor-not-allowed'}"
              aria-label="Toggle WiFi only downloads"
              aria-pressed={cacheConfig.wifiOnly}
              disabled={!networkDetectionSupported}
            >
              <span
                class="inline-block h-6 w-6 transform rounded-full bg-white transition-transform {cacheConfig.wifiOnly
                  ? 'translate-x-7'
                  : 'translate-x-1'}"
              ></span>
            </button>
          </div>
        </div>
      </div>

      <!-- Info Box -->
      <div class="bg-blue-900/20 border border-blue-800 rounded-lg p-4">
        <div class="flex gap-3">
          <svg
            class="w-5 h-5 text-blue-400 flex-shrink-0 mt-0.5"
            fill="currentColor"
            viewBox="0 0 20 20"
          >
            <path
              fill-rule="evenodd"
              d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
              clip-rule="evenodd"
            />
          </svg>
          <div class="text-sm text-blue-300">
            <p class="font-semibold mb-1">About these settings:</p>
            <ul class="list-disc list-inside space-y-1 text-blue-200">
              <li>
                <strong>Crossfade</strong> smoothly blends the end of one track with the
                beginning of the next
              </li>
              <li>
                <strong>Gapless</strong> removes silence between tracks for continuous
                album playback
              </li>
              <li>
                <strong>Normalization</strong> evens out loudness between tracks
                in real time, toward your selected level
              </li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  {/if}
</div>

<style>
  /* Vertical EQ band sliders. `appearance: slider-vertical` is deprecated;
     use writing-mode which is the supported path in modern WebKit/Chromium. */
  .eq-slider {
    writing-mode: vertical-lr;
    direction: rtl;
    width: 8px;
    height: 96px;
    accent-color: var(--color-jellyfin);
    cursor: pointer;
  }
</style>
