/**
 * Mobile-side sxyprn.com resolver.
 *
 * Token wideo sxyprn jest bound do IP które POBRAŁO stronę /post/<id>.html (audit
 * 2026-06-11: ten sam signed URL → 1024B realnego wideo z IP VPS, 10B placeholder z
 * innego IP). Backend resolvuje z VPS → telefon dostaje URL bound do IP VPS → direct
 * daje 10-bajtowy placeholder → ExoPlayer fail → fallback na proxy → CAŁE wideo (setki
 * MB) przez Hetzner przy KAŻDYM playbacku.
 *
 * Fix: telefon sam pobiera stronę (phone IP) → `data-vnfo` token bound do telefonu →
 * transform boo/ssut51 (port z app/extractors/tubes/sxyprn.py) → gra direct, zero VPS.
 * Ten sam wzorzec co pornxpResolver/getfileResolver (resolve od strony, na urządzeniu).
 */
import type { StreamLink } from '../types';

const UA =
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36';

const _VNFO_RE = /data-vnfo='([^']+)'/;
const _B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

/** True dla page_url sxyprn (origin tube:sxyprncom). */
export function isSxyprnUrl(url: string | null | undefined): boolean {
  return !!url && /(?:^|\/\/|\.)sxyprn\.com\//i.test(url);
}

/** Suma cyfr w stringu (ssut51 z main2.js). */
function ssut51(s: string): number {
  let sum = 0;
  for (let i = 0; i < s.length; i++) {
    const c = s.charCodeAt(i);
    if (c >= 48 && c <= 57) sum += c - 48;
  }
  return sum;
}

/** base64-encode ASCII string (self-contained, bez atob/Buffer). */
function b64encode(str: string): string {
  let out = '';
  for (let i = 0; i < str.length; i += 3) {
    const a = str.charCodeAt(i);
    const b = i + 1 < str.length ? str.charCodeAt(i + 1) : NaN;
    const c = i + 2 < str.length ? str.charCodeAt(i + 2) : NaN;
    out += _B64[a >> 2];
    out += _B64[((a & 3) << 4) | (isNaN(b) ? 0 : b >> 4)];
    out += isNaN(b) ? '=' : _B64[((b & 15) << 2) | (isNaN(c) ? 0 : c >> 6)];
    out += isNaN(c) ? '=' : _B64[c & 63];
  }
  return out;
}

/** base64url-safe `<ss>-sxyprn.com-<es>` z `+`→`-`, `/`→`_`, `=`→`.` (boo z main2.js). */
function boo(ss: number, es: number): string {
  return b64encode(`${ss}-sxyprn.com-${es}`)
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '.');
}

/**
 * Pobiera stronę sxyprn NA TELEFONIE i transformuje `data-vnfo` URL-e na grywalne
 * .vid mp4 (phone-IP-bound, grają direct). Zwraca [] gdy brak/dead (caller spada na
 * backend/WebView). Mirror app/extractors/tubes/sxyprn.py.
 */
export async function resolveSxyprnPage(pageUrl: string): Promise<StreamLink[]> {
  let html: string;
  try {
    const r = await fetch(pageUrl, { headers: { 'User-Agent': UA, Accept: 'text/html' } });
    if (!r.ok) return [];
    html = await r.text();
  } catch {
    return [];
  }
  if (html.includes('Post Not Found')) return []; // usunięty post
  const m = _VNFO_RE.exec(html);
  if (!m) return [];
  let vnfo: Record<string, unknown>;
  try {
    vnfo = JSON.parse(m[1]);
  } catch {
    return [];
  }
  const links: StreamLink[] = [];
  for (const src of Object.values(vnfo)) {
    if (typeof src !== 'string' || !src.startsWith('/cdn/')) continue;
    const tmp = src.split('/');
    if (tmp.length < 8) continue;
    const s6 = ssut51(tmp[6]);
    const s7 = ssut51(tmp[7]);
    const ts = parseInt(tmp[5], 10);
    if (Number.isNaN(ts)) continue;
    tmp[1] = tmp[1] + '8' + '/' + boo(s6, s7);
    tmp[5] = String(ts - s6 - s7);
    const full = 'https://sxyprn.com' + tmp.join('/');
    links.push({
      stream_url: full,
      direct_url: full,
      headers: { Referer: 'https://sxyprn.com/', 'User-Agent': UA },
      quality: 'auto',
      type: 'mp4',
    });
  }
  return links;
}
