// Native-video compositing state.
//
// TRACES: UR-003, UR-004 | DR-150, DR-152
//
// Two separate concerns live here, deliberately:
//
//  1. `experimentalNativeVideo` — the user-facing opt-in flag. Rust already
//     decides *which backend this platform has* (`useHtml5Element` from
//     `player_play_item`); this flag only *suppresses* that decision so a
//     half-working spike cannot ship as a regression. It never turns native on
//     where Rust says HTML5.
//
//  2. `nativeVideoActive` — whether a native surface is on screen right now.
//     Setting it toggles `data-native-video` on <html>, which is what the CSS in
//     app.css keys off to clear the app's opaque backgrounds so the SurfaceView
//     behind the WebView is visible. It is deliberately NOT derived from the
//     flag: the backgrounds must come back the moment the player unmounts.
//
// Frontend-only preference, stored in localStorage per the `jellytau-view-mode`
// precedent in library.ts — no Rust settings command backs this.

import { writable } from "svelte/store";

const STORAGE_KEY = "jellytau-experimental-native-video";

/** The attribute app.css keys its transparency rules off. */
const NATIVE_VIDEO_ATTR = "data-native-video";

/**
 * Whether the native path is on, defaulting to **on** when the user has never
 * chosen.
 *
 * This default has moved three times, so the history is the documentation:
 *
 *  - **off** while the path was a spike (DR-150).
 *  - **on** for picture-in-picture (DR-161), which shipped as *audio with no
 *    picture* — ExoPlayer decoded correctly into a live SurfaceView while the
 *    page stayed opaque over it.
 *  - **off** again (DR-172), which named the compositing as the suspect but did
 *    not find it.
 *  - **on** now, because the four defects behind that symptom were found and
 *    each is fixed and verified on a device: the app shell painted over the
 *    surface through a CSS rule targeting an attribute nothing set (DR-185); the
 *    poster card had no way to lift on a path with no `<video>` element
 *    (DR-182); the JS bridges raced the page load, so `setTransparent(true)`
 *    could never arrive (DR-183); and the SurfaceView was never detached
 *    (DR-184). Two further UI defects that only this path could show — the play
 *    overlay never clearing (DR-186) and the system bars staying over the player
 *    (DR-187) — are fixed with it.
 *
 * The picture is genuinely fixed and device-verified — `WebView transparent =
 * true` and `Marking media ready` now appear in logcat with video on screen,
 * the pair DR-172 went looking for and could not find. The default nonetheless
 * stayed **off** for a further release, because turning it on surfaced a
 * different gap: the background-audio handoff (UR-040) could only *return*
 * through the HTML5 element, so coming back from background audio left playback
 * dead. That was the same shape of mistake as DR-161 — a verified sub-path
 * shipped as a default over an unverified one — so the flip waited (DR-190).
 *
 *  - **on** now. The two defects that were holding it back are fixed and
 *    verified on a device: the handoff return restarts the renderer that is
 *    actually on screen rather than only ever reloading the `<video>` element
 *    (DR-196), and the letterbox bars are painted instead of retaining whatever
 *    was last in the framebuffer (DR-194). The evidence standard this default
 *    has been held to since DR-161 is met for both: audio handoff at 69:54
 *    returning to video playing at 70:18, and clean bars across playback, the
 *    control bar and a rotation round-trip.
 *
 * An explicit stored choice still wins in both directions, so anyone who turned
 * it off keeps it off — hence the `null` check rather than a bare `=== "true"`,
 * which would silently re-enable it for people who opted out.
 *
 * TRACES: UR-003, UR-004 | DR-188
 */
function load(): boolean {
  if (typeof localStorage === "undefined") return true;
  try {
    const stored = localStorage.getItem(STORAGE_KEY);
    // Never chosen → on. Chosen → honour it, in both directions.
    return stored === null ? true : stored === "true";
  } catch {
    // Private-mode / disabled storage — same default as a fresh install.
    return true;
  }
}

function persist(enabled: boolean) {
  if (typeof localStorage === "undefined") return;
  try {
    localStorage.setItem(STORAGE_KEY, String(enabled));
  } catch {
    // Quota or private-mode failure — keep the in-memory value.
  }
}

function createExperimentalNativeVideoStore() {
  const { subscribe, set } = writable<boolean>(load());

  return {
    subscribe,
    set(enabled: boolean) {
      persist(enabled);
      set(enabled);
    },
    /** Read the current value without subscribing (init-time decisions). */
    current: load,
  };
}

/**
 * User preference for the native Android video path. **Defaults to on** — see
 * `load()`. The name still says "experimental" because the flag remains a
 * suppressor of Rust's backend choice, not a promoter of it: turning it off
 * forces the webview element, turning it on never produces a native backend
 * where Rust says HTML5.
 */
export const experimentalNativeVideo = createExperimentalNativeVideoStore();

function createNativeVideoActiveStore() {
  const { subscribe, set } = writable<boolean>(false);

  return {
    subscribe,
    /**
     * Mark a native video surface as visible (or gone) and sync the <html>
     * attribute that app.css uses to clear opaque backgrounds.
     */
    set(active: boolean) {
      if (typeof document !== "undefined") {
        if (active) {
          document.documentElement.setAttribute(NATIVE_VIDEO_ATTR, "active");
        } else {
          document.documentElement.removeAttribute(NATIVE_VIDEO_ATTR);
        }
      }
      set(active);
    },
  };
}

/**
 * Whether a native video surface is currently on screen. Must be cleared on
 * player teardown, or the rest of the app renders over a transparent window.
 */
export const nativeVideoActive = createNativeVideoActiveStore();
