/**
 * =====================================================================
 *  Samsung Tizen native AVPlay wrapper
 * =====================================================================
 *  Tizen TVs (2017+, Tizen 3.0+) ship with a hardware-accelerated
 *  player exposed via `window.webapis.avplay`. It supports every codec
 *  the panel itself decodes (H.264, HEVC, VP9, AC-3, EAC-3, AAC,
 *  MPEG-2 TS, …) at up to 4K HDR — far beyond what hls.js/MSE can do.
 *
 *  Render target:
 *    AVPlay does NOT draw into a normal <video> element. Instead it
 *    paints onto an OS-level overlay positioned by setDisplayRect().
 *    The DOM holds an <object type="application/avplayer"> placeholder
 *    so the layout reserves the correct rectangle.
 *
 *  All HTTP requests for HLS chunks go through Tizen's native network
 *  stack (NOT the WebView's fetch), so they bypass file:// CORS rules.
 *  HOWEVER mixed-content is still blocked: stream URLs MUST be https.
 *  We continue to route them through `https://4kmovies.vip/nadiplayer/
 *  api/iptv-proxy` so the User-Agent override and CORS policies stay
 *  consistent with web/Android.
 * =====================================================================
 */

interface AVPlayListener {
  onbufferingstart?: () => void;
  onbufferingcomplete?: () => void;
  onbufferingprogress?: (percent: number) => void;
  oncurrentplaytime?: (currentTime: number) => void;
  onstreamcompleted?: () => void;
  onerror?: (eventType: string) => void;
  onevent?: (eventType: string, data: unknown) => void;
  onsubtitlechange?: (
    duration: number,
    text: string,
    type: number,
    attributeCount: number,
    attributes: unknown
  ) => void;
}

interface AVPlayApi {
  open(url: string): void;
  close(): void;
  prepare(): void;
  prepareAsync(success: () => void, error: (e: unknown) => void): void;
  play(): void;
  pause(): void;
  stop(): void;
  seekTo(ms: number, success?: () => void, error?: () => void): void;
  setDisplayRect(x: number, y: number, w: number, h: number): void;
  setDisplayMethod(method: string): void;
  setListener(l: AVPlayListener): void;
  setStreamingProperty(key: string, value: string): void;
  getCurrentTime(): number;
  getDuration(): number;
  getState(): string;
}

const getAvplay = (): AVPlayApi | null => {
  if (typeof window === "undefined") return null;
  const w = window as unknown as { webapis?: { avplay?: AVPlayApi } };
  return w.webapis?.avplay ?? null;
};

export const isTizenAvplayAvailable = (): boolean => getAvplay() !== null;

export interface TizenPlayOptions {
  url: string;
  userAgent?: string;
  isLive?: boolean;
  onError?: (err: string) => void;
  onEnded?: () => void;
  onBufferingStart?: () => void;
  onBufferingComplete?: () => void;
  onTimeUpdate?: (currentMs: number) => void;
}

let activeListenerOptions: TizenPlayOptions | null = null;

/**
 * Position the AVPlay overlay so it aligns with the placeholder <object>
 * element on the DOM. Call this on mount and on every resize.
 */
export const tizenSetDisplayRect = (el: HTMLElement): void => {
  const av = getAvplay();
  if (!av) return;
  const r = el.getBoundingClientRect();
  // setDisplayRect uses CSS pixels in 1920x1080 logical resolution by default.
  try {
    av.setDisplayRect(
      Math.round(r.left),
      Math.round(r.top),
      Math.round(r.width),
      Math.round(r.height)
    );
  } catch (e) {
    console.warn("[Tizen AVPlay] setDisplayRect failed", e);
  }
};

export const tizenStop = (): void => {
  const av = getAvplay();
  if (!av) return;
  try {
    av.stop();
  } catch (e) {
    console.warn("[Tizen AVPlay] stop failed", e);
  }
  try {
    av.close();
  } catch (e) {
    console.warn("[Tizen AVPlay] close failed", e);
  }
  activeListenerOptions = null;
};

export const tizenPlay = (
  containerEl: HTMLElement,
  options: TizenPlayOptions
): void => {
  const av = getAvplay();
  if (!av) {
    options.onError?.("AVPlay API not available");
    return;
  }

  // Always stop a previous session before opening a new URL — AVPlay is
  // a singleton on Tizen, two open() calls without close() lock it.
  tizenStop();
  activeListenerOptions = options;

  try {
    av.open(options.url);

    // Push the User-Agent through to the native HTTP client. Many IPTV
    // portals reject anything that isn't "VLC" or "IPTV Smarters" with
    // 403 / 401 — same trick we use in the web proxy.
    if (options.userAgent) {
      try {
        av.setStreamingProperty(
          "USER_AGENT",
          options.userAgent
        );
      } catch (e) {
        console.warn("[Tizen AVPlay] setStreamingProperty USER_AGENT failed", e);
      }
    }

    // Cookie / extra-headers placeholder — left empty unless caller adds.
    try {
      av.setStreamingProperty(
        "ADAPTIVE_INFO",
        "BITRATES=auto|STARTBITRATE=LOWEST|SKIPBITRATE=LOWEST"
      );
    } catch (e) {
      console.warn("[Tizen AVPlay] setStreamingProperty ADAPTIVE_INFO failed", e);
    }

    av.setDisplayMethod("PLAYER_DISPLAY_MODE_FULL_SCREEN");
    tizenSetDisplayRect(containerEl);

    av.setListener({
      onbufferingstart: () => activeListenerOptions?.onBufferingStart?.(),
      onbufferingcomplete: () => activeListenerOptions?.onBufferingComplete?.(),
      oncurrentplaytime: (ms) => activeListenerOptions?.onTimeUpdate?.(ms),
      onstreamcompleted: () => activeListenerOptions?.onEnded?.(),
      onerror: (e) => {
        console.error("[Tizen AVPlay] error event:", e);
        activeListenerOptions?.onError?.(String(e));
      },
    });

    av.prepareAsync(
      () => {
        try {
          av.play();
        } catch (e) {
          console.error("[Tizen AVPlay] play() threw:", e);
          options.onError?.(`play failed: ${String(e)}`);
        }
      },
      (err) => {
        console.error("[Tizen AVPlay] prepareAsync failed:", err);
        options.onError?.(`prepare failed: ${String(err)}`);
      }
    );
  } catch (e) {
    console.error("[Tizen AVPlay] open/prepare threw:", e);
    options.onError?.(`open failed: ${String(e)}`);
  }
};

export const tizenPause = (): void => {
  const av = getAvplay();
  if (!av) return;
  try {
    av.pause();
  } catch (e) {
    console.warn("[Tizen AVPlay] pause failed", e);
  }
};

export const tizenResume = (): void => {
  const av = getAvplay();
  if (!av) return;
  try {
    av.play();
  } catch (e) {
    console.warn("[Tizen AVPlay] resume failed", e);
  }
};

export const tizenSeekMs = (ms: number): void => {
  const av = getAvplay();
  if (!av) return;
  try {
    av.seekTo(ms);
  } catch (e) {
    console.warn("[Tizen AVPlay] seek failed", e);
  }
};
