// Define an interface for the browser result
interface BrowserResult {
    name: string;
    version: string;
  }

// Browser detection strategy for privacy browsers that mask their user agent
interface BrowserDetectionStrategy {
  name: string;
  detect: () => boolean;
}

// Module-scoped cache keyed on brave-marker + UA string.
// Brave masks its UA so we must distinguish brave vs non-brave for the same UA.
const browserCache = new Map<string, BrowserResult>();

/** Test-only: reset the memoization cache between test runs. */
export function __resetBrowserCache(): void {
  browserCache.clear();
}

// Define a function to parse the user agent string and return the browser name and version
export function getBrowser(): BrowserResult {
  if (typeof navigator === 'undefined') {
    return {
      name: "unknown",
      version: "unknown"
    }
  }

  const isBrave = !!(navigator as any).brave;
  const cacheKey = (isBrave ? 'B|' : 'N|') + navigator.userAgent;
  const cached = browserCache.get(cacheKey);
  if (cached) return cached;

  // Check for privacy browsers first (these mask their user agent)
  const privacyBrowserStrategies: BrowserDetectionStrategy[] = [
    {
      name: 'Brave',
      detect: () => isBrave
    }
  ];

  let result: BrowserResult;

  for (const strategy of privacyBrowserStrategies) {
    if (strategy.detect()) {
      const parsed = parseBrowserFromUA(navigator.userAgent);
      result = { name: strategy.name, version: parsed.version };
      browserCache.set(cacheKey, result);
      return result;
    }
  }

  // Fall back to user agent parsing for standard browsers
  result = parseBrowserFromUA(navigator.userAgent);
  browserCache.set(cacheKey, result);
  return result;
}

// Parse browser from user agent string
function parseBrowserFromUA(ua: string): BrowserResult {
  // DeviceAtlas authoritative regex order and patterns
  const regexes = [
    // Samsung Internet (Android)
    /(?<name>SamsungBrowser)\/(?<version>\d+(?:\.\d+)+)/,
    // Edge (Chromium, Android, iOS)
    /(?<name>EdgA|EdgiOS|Edg)\/(?<version>\d+(?:\.\d+)+)/,
    // Opera (OPR, OPX, Opera Mini, Opera Mobi)
    /(?<name>OPR|OPX)\/(?<version>\d+(?:\.\d+)+)/,
    /Opera[\s\/](?<version>\d+(?:\.\d+)+)/,
    /Opera Mini\/(?<version>\d+(?:\.\d+)+)/,
    /Opera Mobi\/(?<version>\d+(?:\.\d+)+)/,
    // Vivaldi
    /(?<name>Vivaldi)\/(?<version>\d+(?:\.\d+)+)/,
    // Chrome iOS (CriOS)
    /(?<name>CriOS)\/(?<version>\d+(?:\.\d+)+)/,
    // Firefox iOS (FxiOS)
    /(?<name>FxiOS)\/(?<version>\d+(?:\.\d+)+)/,
    // Chrome, Chromium (desktop & Android)
    /(?<name>Chrome|Chromium)\/(?<version>\d+(?:\.\d+)+)/,
    // Firefox (desktop & Android)
    /(?<name>Firefox|Waterfox|Iceweasel|IceCat)\/(?<version>\d+(?:\.\d+)+)/,
    // Safari (desktop & iOS): prefer Version/x.y if present, else Safari/x.y
    /Version\/(?<version1>[\d.]+).*Safari\/[\d.]+|(?<name>Safari)\/(?<version2>[\d.]+)/,
    // Internet Explorer, IE Mobile
    /(?<name>MSIE|Trident|IEMobile).+?(?<version>\d+(?:\.\d+)+)/,
    // Other browsers that use the format "BrowserName/version"
    /(?<name>[A-Za-z]+)\/(?<version>\d+(?:\.\d+)+)/,
  ];

  // Map UA tokens to canonical browser names
  const browserNameMap: { [key: string]: string } = {
    'edg': 'Edge',
    'edga': 'Edge',
    'edgios': 'Edge',
    'opr': 'Opera',
    'opx': 'Opera',
    'crios': 'Chrome',
    'fxios': 'Firefox',
    'samsung': 'SamsungBrowser',
    'vivaldi': 'Vivaldi',
  };

  for (const regex of regexes) {
    const match = ua.match(regex);
    if (match) {
      let name = match.groups?.name;
      let version = match.groups?.version || match.groups?.version1 || match.groups?.version2;
      // For Safari, if Version/x.y matched, set name to Safari
      if (!name && (match.groups?.version1 || match.groups?.version2)) name = 'Safari';
      // Fallbacks for legacy Opera/Opera Mini/Opera Mobi
      if (!name && regex.source.includes('Opera Mini')) name = 'Opera Mini';
      if (!name && regex.source.includes('Opera Mobi')) name = 'Opera Mobi';
      if (!name && regex.source.includes('Opera')) name = 'Opera';
      // Fallback for generic [A-Za-z]+/version
      if (!name && match[1]) name = match[1];
      if (!version && match[2]) version = match[2];
      if (name) {
        const canonical = browserNameMap[name.toLowerCase()] || name;
        return { name: canonical, version: version || 'unknown' };
      }
    }
  }
  return {
    name: "unknown",
    version: "unknown"
  };
}

// Utility function to detect if a user agent is a mobile device (DeviceAtlas-style)
export function isMobileUserAgent(): boolean {
  if (typeof navigator === 'undefined' || !navigator.userAgent) return false;
  const ua = navigator.userAgent;
  // Exclude iPad from 'mobile' (treat as tablet)
  return /Mobi|Android|iPhone|iPod|IEMobile|Opera Mini|Opera Mobi|webOS|BlackBerry|Windows Phone/i.test(ua)
    && !/iPad/i.test(ua);
}
  
