{ lib }:

let
  inherit (lib)
    any
    attrValues
    concatMap
    filter
    hasPrefix
    isList
    mapAttrs
    matchAttrs
    recursiveUpdateUntil
    toList
    ;

  inherit (lib.strings) toJSON;

  inherit (lib.systems.parse)
    kernels
    kernelFamilies
    significantBytes
    cpuTypes
    execFormats
    ;

  abis = mapAttrs (_: abi: removeAttrs abi [ "assertions" ]) lib.systems.parse.abis;
in

rec {
  # these patterns are to be matched against {host,build,target}Platform.parsed
  patterns = rec {
    # The patterns below are lists in sum-of-products form.
    #
    # Each attribute is list of product conditions; non-list values are treated
    # as a singleton list.  If *any* product condition in the list matches then
    # the predicate matches.  Each product condition is tested by
    # `lib.attrsets.matchAttrs`, which requires a match on *all* attributes of
    # the product.

    isi686         = { cpu = cpuTypes.i686; };
    isx86_32       = { cpu = { family = "x86"; bits = 32; }; };
    isx86_64       = { cpu = { family = "x86"; bits = 64; }; };
    isPower        = { cpu = { family = "power"; }; };
    isPower64      = { cpu = { family = "power"; bits = 64; }; };
    # This ABI is the default in NixOS PowerPC64 BE, but not on mainline GCC,
    # so it sometimes causes issues in certain packages that makes the wrong
    # assumption on the used ABI.
    isAbiElfv2 = [
      { abi = { abi = "elfv2"; }; }
      { abi = { name = "musl"; }; cpu = { family = "power"; bits = 64; }; }
    ];
    isx86          = { cpu = { family = "x86"; }; };
    isAarch32      = { cpu = { family = "arm"; bits = 32; }; };
    isArmv7        = map ({ arch, ... }: { cpu = { inherit arch; }; })
                       (filter (cpu: hasPrefix "armv7" cpu.arch or "")
                         (attrValues cpuTypes));
    isAarch64      = { cpu = { family = "arm"; bits = 64; }; };
    isAarch        = { cpu = { family = "arm"; }; };
    isMicroBlaze   = { cpu = { family = "microblaze"; }; };
    isMips         = { cpu = { family = "mips"; }; };
    isMips32       = { cpu = { family = "mips"; bits = 32; }; };
    isMips64       = { cpu = { family = "mips"; bits = 64; }; };
    isMips64n32    = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "n32"; }; };
    isMips64n64    = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "64";  }; };
    isMmix         = { cpu = { family = "mmix"; }; };
    isRiscV        = { cpu = { family = "riscv"; }; };
    isRiscV32      = { cpu = { family = "riscv"; bits = 32; }; };
    isRiscV64      = { cpu = { family = "riscv"; bits = 64; }; };
    isRx           = { cpu = { family = "rx"; }; };
    isSparc        = { cpu = { family = "sparc"; }; };
    isSparc64      = { cpu = { family = "sparc"; bits = 64; }; };
    isWasm         = { cpu = { family = "wasm"; }; };
    isMsp430       = { cpu = { family = "msp430"; }; };
    isVc4          = { cpu = { family = "vc4"; }; };
    isAvr          = { cpu = { family = "avr"; }; };
    isAlpha        = { cpu = { family = "alpha"; }; };
    isOr1k         = { cpu = { family = "or1k"; }; };
    isM68k         = { cpu = { family = "m68k"; }; };
    isS390         = { cpu = { family = "s390"; }; };
    isS390x        = { cpu = { family = "s390"; bits = 64; }; };
    isLoongArch64  = { cpu = { family = "loongarch"; bits = 64; }; };
    isJavaScript   = { cpu = cpuTypes.javascript; };

    is32bit        = { cpu = { bits = 32; }; };
    is64bit        = { cpu = { bits = 64; }; };
    isILP32        = [ { cpu = { family = "wasm"; bits = 32; }; } ] ++
                     map (a: { abi = { abi = a; }; }) [ "n32" "ilp32" "x32" ];
    isBigEndian    = { cpu = { significantByte = significantBytes.bigEndian; }; };
    isLittleEndian = { cpu = { significantByte = significantBytes.littleEndian; }; };

    isBSD          = { kernel = { families = { inherit (kernelFamilies) bsd; }; }; };
    isDarwin       = { kernel = { families = { inherit (kernelFamilies) darwin; }; }; };
    isUnix         = [ isBSD isDarwin isLinux isSunOS isCygwin isRedox ];

    isMacOS        = { kernel = kernels.macos; };
    isiOS          = { kernel = kernels.ios; };
    isLinux        = { kernel = kernels.linux; };
    isSunOS        = { kernel = kernels.solaris; };
    isFreeBSD      = { kernel = { name = "freebsd"; }; };
    isNetBSD       = { kernel = kernels.netbsd; };
    isOpenBSD      = { kernel = kernels.openbsd; };
    isWindows      = { kernel = kernels.windows; };
    isCygwin       = { kernel = kernels.windows; abi = abis.cygnus; };
    isMinGW        = { kernel = kernels.windows; abi = abis.gnu; };
    isWasi         = { kernel = kernels.wasi; };
    isRedox        = { kernel = kernels.redox; };
    isGhcjs        = { kernel = kernels.ghcjs; };
    isGenode       = { kernel = kernels.genode; };
    isNone         = { kernel = kernels.none; };

    isAndroid      = [ { abi = abis.android; } { abi = abis.androideabi; } ];
    isGnu          = with abis; map (a: { abi = a; }) [ gnuabi64 gnuabin32 gnu gnueabi gnueabihf gnuabielfv1 gnuabielfv2 ];
    isMusl         = with abis; map (a: { abi = a; }) [ musl musleabi musleabihf muslabin32 muslabi64 ];
    isUClibc       = with abis; map (a: { abi = a; }) [ uclibc uclibceabi uclibceabihf ];

    isEfi = [
      { cpu = { family = "arm"; version = "6"; }; }
      { cpu = { family = "arm"; version = "7"; }; }
      { cpu = { family = "arm"; version = "8"; }; }
      { cpu = { family = "riscv"; }; }
      { cpu = { family = "x86"; }; }
    ];

    isElf          = { kernel.execFormat = execFormats.elf; };
    isMacho        = { kernel.execFormat = execFormats.macho; };
  };

  # given two patterns, return a pattern which is their logical AND.
  # Since a pattern is a list-of-disjuncts, this needs to
  patternLogicalAnd = pat1_: pat2_:
    let
      # patterns can be either a list or a (bare) singleton; turn
      # them into singletons for uniform handling
      pat1 = toList pat1_;
      pat2 = toList pat2_;
    in
      concatMap (attr1:
        map (attr2:
          recursiveUpdateUntil
            (path: subattr1: subattr2:
              if (builtins.intersectAttrs subattr1 subattr2) == {} || subattr1 == subattr2
              then true
              else throw ''
                pattern conflict at path ${toString path}:
                  ${toJSON subattr1}
                  ${toJSON subattr2}
                '')
            attr1
            attr2
            )
          pat2)
        pat1;

  matchAnyAttrs = patterns:
    if isList patterns then attrs: any (pattern: matchAttrs pattern attrs) patterns
    else matchAttrs patterns;

  predicates = mapAttrs (_: matchAnyAttrs) patterns;

  # these patterns are to be matched against the entire
  # {host,build,target}Platform structure; they include a `parsed={}` marker so
  # that `lib.meta.availableOn` can distinguish them from the patterns which
  # apply only to the `parsed` field.

  platformPatterns = mapAttrs (_: p: { parsed = {}; } // p) {
    isStatic = { isStatic = true; };
  };
}
