// @ts-ignore
export const isBun = typeof Bun !== 'undefined'

// @ts-ignore
export const isElectron = typeof process !== 'undefined' && process.versions?.electron !== undefined

// @ts-ignore
export const isDeno = typeof Deno !== 'undefined'

export const isBrowser =
  typeof navigator !== 'undefined' && navigator.userAgent?.startsWith?.('Mozilla/5.0 ')

export const isWorkerd =
  typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers'

// @ts-ignore
export const isNode =
  !isBun && !isElectron && !isDeno && !isWorkerd && typeof process !== 'undefined'

const BOWSER = 'https://cdn.jsdelivr.net/npm/bowser@2.11.0/src/bowser.js'

let parsedUserAgent: any
async function parseUserAgent() {
  const { default: Bowser } = await import(BOWSER)
  parsedUserAgent || (parsedUserAgent = Bowser.parse(window.navigator.userAgent))
  return parsedUserAgent
}

async function isEngine(engine: string) {
  const userAgentData = await parseUserAgent()
  return userAgentData.engine.name === engine
}

export function isBrowserVersionAtLeast(version: number) {
  if (!parsedUserAgent) throw new Error()
  return parseInt(parsedUserAgent.browser.version.split('.')[0], 10) >= version
}

export const isBlink = isBrowser && (await isEngine('Blink'))

export const isWebKit = isBrowser && (await isEngine('WebKit'))

export const isGecko = isBrowser && (await isEngine('Gecko'))

function isNodeVersionAtLeast(major: number, minor: number) {
  const parts = process.versions.node.split('.').map((i) => parseInt(i, 10))
  return parts[0] > major || (parts[0] === major && parts[1] >= minor)
}

function isElectronChromiumVersionAtLeast(major: number) {
  const version = (process.versions as Record<string, string | undefined>).chrome
  return typeof version === 'string' && parseInt(version.split('.')[0], 10) >= major
}

export function supported(identifier?: string, op?: string) {
  switch (identifier) {
    case 'RSA1_5':
    case 'X448':
    case 'Ed448':
    case 'ES256K':
    case 'secp256k1':
      return false
  }

  switch (identifier) {
    case 'ML-DSA-44':
    case 'ML-DSA-65':
    case 'ML-DSA-87':
      if (isElectron) {
        if (!isNodeVersionAtLeast(24, 18)) {
          return false
        }
        switch (op) {
          case 'private jwk import':
          case 'pem import':
            return true
          default:
            return isElectronChromiumVersionAtLeast(150)
        }
      }
      return isBun || isDeno || (isNode && isNodeVersionAtLeast(24, 7))
  }

  if (isBlink) {
    switch (identifier) {
      case 'A192CBC-HS384':
      case 'A192GCM':
      case 'A192GCMKW':
      case 'A192KW':
      case 'PBES2-HS384+A192KW':
        return false
    }
  }

  if (isElectron) {
    switch (identifier) {
      case 'A128KW':
      case 'A192KW':
      case 'A256KW':
      case 'PBES2-HS256+A128KW':
      case 'PBES2-HS384+A192KW':
      case 'PBES2-HS512+A256KW':
      case 'ECDH-ES+A128KW':
      case 'ECDH-ES+A192KW':
      case 'ECDH-ES+A256KW':
        return isNodeVersionAtLeast(24, 18)
    }
  }

  return true
}
