/**
 * Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
 */

type NavigatorUAData = {
  brands?: {brand: string; version: string}[];
  mobile?: boolean;
  platform?: string;
  getHighEntropyValues?: (hints: string[]) => Promise<Record<string, string>>;
  toJSON?: () => object;
};

export default class PlatformDetectionService {
  private static readonly _userAgent: string = globalThis.navigator?.userAgent ?? '';
  // @ts-expect-error NavigatorUAData is experimental and not defined in the lib dom yet https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgentData
  private static readonly _userAgentData: NavigatorUAData | undefined = globalThis.navigator?.userAgentData;

  private static readonly _areClientHintsSupported: boolean = !!PlatformDetectionService._userAgentData;
  private static _platform: string = 'Unknown';
  private static _platformVersion: string = '';
  private static _browserName: string = 'Unknown';
  private static _browserVersion: string = '?';
  private static _isWebview: boolean = false;

  static {
    if (PlatformDetectionService._areClientHintsSupported) {
      PlatformDetectionService.initFromClientHints();
    } else {
      PlatformDetectionService.initFromUserAgent();
    }
  }

  private constructor() {
    throw new Error('PlatformDetectionService is a static class that may not be instantiated');
  }

  // ---- Public API ----
  static get platform(): string {
    return PlatformDetectionService._platform;
  }
  static get platformVersion(): string {
    return PlatformDetectionService._platformVersion;
  }
  static get userAgent(): string {
    return PlatformDetectionService._userAgent;
  }
  static get browserName(): string {
    return PlatformDetectionService._browserName;
  }
  static get browserVersion(): string {
    return PlatformDetectionService._browserVersion;
  }
  static get isWebview(): boolean {
    return PlatformDetectionService._isWebview;
  }
  static get areClientHintsSupported(): boolean {
    return PlatformDetectionService._areClientHintsSupported;
  }

  /**
   * Optional async initialization for high-entropy values like platformVersion
   */
  static async initAsync(): Promise<void> {
    if (PlatformDetectionService._areClientHintsSupported && PlatformDetectionService._userAgentData?.getHighEntropyValues) {
      const values = await PlatformDetectionService._userAgentData.getHighEntropyValues(['platformVersion']);

      if (values.platformVersion) {
        PlatformDetectionService._platformVersion = values.platformVersion;
      }
    }
  }

  // ---- Init strategies ----
  private static initFromClientHints() {
    const data = PlatformDetectionService._userAgentData as NavigatorUAData;
    const nonChromiumBrand = data.brands?.find(b => b.brand !== 'Chromium');

    PlatformDetectionService._browserName = nonChromiumBrand?.brand ?? 'Unknown';
    PlatformDetectionService._browserVersion = nonChromiumBrand?.version ?? '?';
    PlatformDetectionService._platform = data.platform ?? 'Unknown';
    PlatformDetectionService._isWebview = PlatformDetectionService.extractIsWebviewFromUserAgent(); // Fallback check
  }

  private static initFromUserAgent() {
    PlatformDetectionService._platform = PlatformDetectionService.extractPlatformFromUserAgent();
    PlatformDetectionService._platformVersion = PlatformDetectionService.extractPlatformVersionFromUserAgent();
    PlatformDetectionService._browserName = PlatformDetectionService.extractBrowserNameFromUserAgent();
    PlatformDetectionService._browserVersion = PlatformDetectionService.extractBrowserVersionFromUserAgent();
    PlatformDetectionService._isWebview = PlatformDetectionService.extractIsWebviewFromUserAgent();
  }

  // ---- Helpers ----
  private static extractBrowserNameFromUserAgent(): string {
    if (/Edg\//.test(PlatformDetectionService._userAgent)) {
      return 'Edge';
    }

    if (/OPR\//.test(PlatformDetectionService._userAgent)) {
      return 'Opera';
    }

    if (/Firefox\//.test(PlatformDetectionService._userAgent)) {
      return 'Firefox';
    }

    if (/Trident\/.*rv:/.test(PlatformDetectionService._userAgent)) {
      return 'IE';
    }

    if (/Chrome\//.test(PlatformDetectionService._userAgent)) {
      return 'Chrome';
    }

    if (/Safari\//.test(PlatformDetectionService._userAgent)) {
      return 'Safari';
    }

    if (/ReactNative\//.test(PlatformDetectionService._userAgent)) {
      return 'ReactNative';
    }

    return 'Unknown';
  }

  private static extractBrowserVersionFromUserAgent(): string {
    return (
      PlatformDetectionService.matchVersion(/Edg\/([\d.]+)/) ??
      PlatformDetectionService.matchVersion(/OPR\/([\d.]+)/) ??
      PlatformDetectionService.matchVersion(/Firefox\/([\d.]+)/) ??
      PlatformDetectionService.matchVersion(/rv:([\d.]+)/) ?? // IE
      PlatformDetectionService.matchVersion(/Chrome\/([\d.]+)/) ??
      PlatformDetectionService.matchVersion(/Version\/([\d.]+)/) ?? // Safari often uses "Version/"
      PlatformDetectionService.matchVersion(/Safari\/([\d.]+)/) ??
      PlatformDetectionService.matchVersion(/ReactNative\/([\d.]+)/) ??
      '?'
    );
  }

  private static extractPlatformFromUserAgent(): string {
    if (/Windows/.test(PlatformDetectionService._userAgent)) {
      return 'Windows';
    }

    if (/iPhone|iPad|iPod/.test(PlatformDetectionService._userAgent)) {
      return 'iOS';
    }

    if (/Mac OS X/.test(PlatformDetectionService._userAgent)) {
      return 'macOS';
    }

    if (/Android/.test(PlatformDetectionService._userAgent)) {
      return 'Android';
    }

    if (/Linux/.test(PlatformDetectionService._userAgent)) {
      return 'Linux';
    }

    return 'Unknown';
  }

  private static extractPlatformVersionFromUserAgent(): string {
    switch (PlatformDetectionService._platform) {
      case 'Windows':
        return PlatformDetectionService.matchVersion(/Windows NT ([\d.]+)/) ?? '';
      case 'iOS':
        return PlatformDetectionService.matchVersion(/OS ([\d_]+)/)?.replace(/_/g, '.') ?? '';
      case 'macOS':
        return PlatformDetectionService.matchVersion(/Mac OS X ([\d_]+)/)?.replace(/_/g, '.') ?? '';
      case 'Android':
        return PlatformDetectionService.matchVersion(/Android ([\d.]+)/) ?? '';
      default:
        return '';
    }
  }

  private static extractIsWebviewFromUserAgent(): boolean {
    return (
      /; wv/.test(PlatformDetectionService._userAgent) || // Android webview
      (/Android/.test(PlatformDetectionService._userAgent) && /Version\/[\d.]+/.test(PlatformDetectionService._userAgent)) || // Some Android webviews
      /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/.test(PlatformDetectionService._userAgent) // IOS webview
    );
  }

  private static matchVersion(pattern: RegExp): string | null {
    const match = PlatformDetectionService._userAgent.match(pattern);

    return match ? match[1] : null;
  }
}