/**
 * browser-features.js (Issue #453)
 *
 * ブラウザ依存挙動を扱う際の唯一の正規ソース。
 *
 * 設計方針:
 *   1. **capability (feature detection) を最優先**で使う。
 *      動作判定が必要な箇所は `features.hasX` を見て分岐すること。
 *      ─ `IS_ANDROID` / `IS_IOS_SAFARI` 等の platform 判定を
 *        gate keeping に使うと iPad desktop mode / Chrome iOS / WebView /
 *        Facebook in-app browser 等の誤判定を生む。
 *   2. **platform labels は telemetry / 既知 quirk の最終手段としてのみ**使う。
 *      `platform.isIOS` 等は logging やパフォーマンス計測のセグメント、
 *      もしくは「iOS Safari の WebRTC 音声を `<audio>` 経由ではなく
 *      remote AudioContext 経由で再生する」のような OS 固有 quirk への対処に限定する。
 *      新規コードでは可能な限り capability を優先すること。
 *
 * Mockability:
 *   Jest からは `navigator` をモックすれば各値の挙動を再現できる。
 *   `IS_IOS` 等のモジュールトップ const を直接 export していた旧実装は、
 *   モジュール load 時に `navigator.userAgent` を 1 回だけ読むため
 *   テスト時の差し替えが効きにくかった。本モジュールは getter で都度評価する。
 */

// ----------------------------------------------------------------------------
// helpers
// ----------------------------------------------------------------------------

function getUA() {
  if (typeof navigator === "undefined") return "";
  return navigator.userAgent || "";
}

function getPlatform() {
  if (typeof navigator === "undefined") return "";
  return navigator.platform || "";
}

function getMaxTouchPoints() {
  if (typeof navigator === "undefined") return 0;
  return navigator.maxTouchPoints || 0;
}

function hasGlobal(name) {
  return typeof globalThis !== "undefined" && typeof globalThis[name] !== "undefined";
}

// ----------------------------------------------------------------------------
// Platform labels — telemetry / 既知 quirk 対応専用
// ----------------------------------------------------------------------------

/**
 * iOS / iPadOS WebKit のいずれかと判定される。
 * - 古来の iPhone/iPad/iPod UA
 * - iPad desktop mode (UA は MacIntel) は `MacIntel + maxTouchPoints > 1` で識別
 * iOS 上の Chrome / Edge / Firefox 等もすべて WebKit 由来なので true になる。
 */
export function isIOS() {
  const ua = getUA();
  if (/iPad|iPhone|iPod/.test(ua)) return true;
  // iPadOS desktop mode (UA が Mac として偽装される)
  return getPlatform() === "MacIntel" && getMaxTouchPoints() > 1;
}

/**
 * Android (WebView 含む)。
 * Android WebView は UA に "wv" を含むが、Chromium 由来として通常の Android 扱いで OK。
 */
export function isAndroid() {
  return /Android/i.test(getUA());
}

/**
 * Safari (WebKit) と判定される UA。
 * - macOS Safari
 * - iOS Safari
 * - iOS 上の Chrome (CriOS) / Edge (EdgiOS) / Firefox (FxiOS): いずれも WebKit を
 *   間借りしているため Safari と同じ quirk を持つ。本関数は true を返す
 *   (旧実装の negative-lookahead Chrome 除外 regex は CriOS / EdgiOS / FxiOS を
 *   Safari 判定から漏らしていた既知バグを修正)。
 */
export function isSafari() {
  if (isIOS()) return true; // iOS は事実上 WebKit
  const ua = getUA();
  // デスクトップ Chrome / Chromium / Android Chrome を除外すれば、Chromium Edge
  // (Edg/) も旧 EdgeHTML (Edge/) も実 UA は同時に `Chrome/...Safari/` を含むため
  // 同時に除外される。
  // PR #538 v1: 元コードの否定先読みはデッドコードのため除去
  // PR #538 v4 AI Review low: `Edge?\/` パターンは実 UA では Chrome match が
  // 先に取るため事実上デッドコード。可読性のため除去し、
  // 「Edge を Chrome 経由で間接除外している」前提をコメントで明示
  if (/Chrome|Chromium|Android/i.test(ua)) return false;
  return /Safari/i.test(ua);
}

/**
 * iOS WebKit (= iOS Safari 系)。
 * audio playback / autoplay restrictions / AudioContext resume() 等の OS 固有 quirk
 * に対処するための gate に使う。
 */
export function isIOSSafari() {
  return isIOS() && isSafari();
}

/**
 * Android WebView (FB / LINE 等の in-app browser を含む)。
 * - Android UA かつ "; wv)" を含む → 標準 Android WebView
 * - Facebook (FBAN / FBAV) / LINE (Line/) / Twitter (Twitter for) 系 in-app browser
 *   は autoplay / permission 制約が独自なので別途警告したい
 */
export function isAndroidWebView() {
  const ua = getUA();
  if (!isAndroid()) return false;
  if (/;\s*wv\)/.test(ua)) return true;
  return /FBAN|FBAV|Line\/|Twitter for/i.test(ua);
}

// ----------------------------------------------------------------------------
// Capability detection (feature flags) — 動作分岐の正本
// ----------------------------------------------------------------------------

/** WebRTC PeerConnection が使えるか */
export function hasWebRTC() {
  return hasGlobal("RTCPeerConnection");
}

/** AudioContext (標準 or webkit prefix) が使えるか */
export function hasAudioContext() {
  if (typeof window === "undefined") return false;
  return typeof window.AudioContext !== "undefined" ||
    typeof window.webkitAudioContext !== "undefined";
}

/** AudioWorklet が使えるか (audioContext.audioWorklet 経由) */
export function hasAudioWorklet() {
  if (typeof window === "undefined") return false;
  const AC = window.AudioContext || window.webkitAudioContext;
  if (!AC) return false;
  // PR #538 v6 E2E 修正: `AC.prototype.audioWorklet` を直接読むと Chrome が
  // "Illegal invocation" を投げる (getter が AudioContext instance を要求するため)。
  // `'audioWorklet' in AC.prototype` ならプロパティ定義の有無だけを確認でき、
  // getter は起動しない。これでロード時の page-level error を防ぐ。
  return AC.prototype ? "audioWorklet" in AC.prototype : false;
}

/** navigator.mediaDevices.getUserMedia (マイク / カメラアクセス) */
export function hasMediaDevices() {
  if (typeof navigator === "undefined") return false;
  return Boolean(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
}

/** BroadcastChannel (多重タブ排他 / IPC) */
export function hasBroadcastChannel() {
  return hasGlobal("BroadcastChannel");
}

/** crypto.randomUUID */
export function hasRandomUUID() {
  if (typeof crypto === "undefined") return false;
  return typeof crypto.randomUUID === "function";
}

/** Web Speech API SpeechSynthesis (TTS fallback として使う) */
export function hasSpeechSynthesis() {
  return hasGlobal("speechSynthesis");
}

/**
 * Network Information API (`navigator.connection`).
 * Safari は未対応のため、ここが false ならアプリ側で実測 fallback する想定。
 */
export function hasNetworkInformation() {
  if (typeof navigator === "undefined") return false;
  return Boolean(navigator.connection);
}

/** CSS.supports() — CSS 機能の動的判定 */
export function cssSupports(property, value) {
  if (typeof CSS === "undefined" || typeof CSS.supports !== "function") return false;
  try {
    return CSS.supports(property, value);
  } catch (_e) {
    return false;
  }
}

// ----------------------------------------------------------------------------
// Aggregate snapshot — logging / telemetry 1 行で出すための便利関数
// ----------------------------------------------------------------------------

/**
 * デバッグログ等で 1 オブジェクトに platform + capability を出したいときに使う。
 * **判定ロジックには使わず**、telemetry 専用。
 */
export function snapshot() {
  return {
    platform: {
      isIOS: isIOS(),
      isIOSSafari: isIOSSafari(),
      isAndroid: isAndroid(),
      isAndroidWebView: isAndroidWebView(),
      isSafari: isSafari(),
    },
    capabilities: {
      hasWebRTC: hasWebRTC(),
      hasAudioContext: hasAudioContext(),
      hasAudioWorklet: hasAudioWorklet(),
      hasMediaDevices: hasMediaDevices(),
      hasBroadcastChannel: hasBroadcastChannel(),
      hasSpeechSynthesis: hasSpeechSynthesis(),
      hasNetworkInformation: hasNetworkInformation(),
      hasRandomUUID: hasRandomUUID(),
    },
  };
}
