// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { ANY, INVALID } from "./constants.ts";
import type { SemVer } from "./types.ts";
import { isValidNumber, isValidString } from "./_shared.ts";

/**
 * Checks to see if value is a valid SemVer object. It does a check
 * into each field including prerelease and build.
 *
 * Some invalid SemVer sentinels can still return true such as ANY and INVALID.
 * An object which has the same value as a sentinel but isn't reference equal
 * will still fail.
 *
 * Objects which are valid SemVer objects but have _extra_ fields are still
 * considered SemVer objects and this will return true.
 *
 * A type assertion is added to the value.
 *
 * @example Usage
 * ```ts
 * import { isSemVer } from "@std/semver/is-semver";
 * import { assert, assertFalse } from "@std/assert";
 *
 * const value = {
 *   major: 1,
 *   minor: 2,
 *   patch: 3,
 * };
 *
 * assert(isSemVer(value));
 * assertFalse(isSemVer({ major: 1, minor: 2 }));
 * ```
 *
 * @param value The value to check to see if its a valid SemVer object
 * @returns True if value is a valid SemVer otherwise false
 */
export function isSemVer(value: unknown): value is SemVer {
  if (value === null || value === undefined) return false;
  if (Array.isArray(value)) return false;
  if (typeof value !== "object") return false;
  if (value === INVALID) return true;
  if (value === ANY) return true;

  const {
    major,
    minor,
    patch,
    build = [],
    prerelease = [],
  } = value as Record<string, unknown>;
  return (
    isValidNumber(major) &&
    isValidNumber(minor) &&
    isValidNumber(patch) &&
    Array.isArray(prerelease) &&
    prerelease.every((v) => isValidString(v) || isValidNumber(v)) &&
    Array.isArray(build) &&
    build.every(isValidString)
  );
}

// denoCacheMetadata={"headers":{"content-type":"text/typescript","cf-cache-status":"HIT","access-control-expose-headers":"*","server":"cloudflare","accept-ranges":"bytes","access-control-allow-origin":"*","cache-control":"public, max-age=31536000, immutable","cf-ray":"a223e657ca707db6-LAX","content-security-policy":"default-src 'none'; script-src 'none'; style-src 'none'; img-src 'none'; font-src 'none'; connect-src 'none'; frame-src 'none'; object-src 'none'; frame-ancestors 'none'; sandbox; form-action 'none';","content-length":"1849","date":"Tue, 28 Jul 2026 12:31:15 GMT","etag":"\"7429c302ceb38d9e2971f4f16277c1ed\"","vary":"Origin","x-jsr-backend":"modules","x-robots-tag":"noindex","set-cookie":"__cf_bm=ntQThcHHA87NgjdAFSoA4tB76VZkYUs_paLQnT2cVTo-1785241875.1634424-1.0.1.1-ctnOaXoCHqvFrFSG0LDs9o9JwhoSlJNhJVz4OMYHQw5BVNnkmtGKWT5BoD8YSylLRlIwIY7xEatmvgYtus8Cl4em5SQIBSqcozejrnsH0C_WdTBgGmr5Votyd1dicAbM; HttpOnly; SameSite=None; Secure; Path=/; Domain=jsr.io; Expires=Tue, 28 Jul 2026 13:01:15 GMT","report-to":"{\"group\":\"cf-nel\",\"max_age\":604800,\"endpoints\":[{\"url\":\"https://a.nel.cloudflare.com/report/v4?s=BDHQEkgJ3aMbGoL%2FEaKXrzhPWWRqy9IqkBc0dXYriZ8vpHwGwqlOFuPammFAjdHKkq8vkj8NuIcAcLozFOd2HqVRiUg3PAKLHpzyvyFQre%2BRJSvuJlAh2rA%3D\"}]}","age":"36703","alt-svc":"h3=\":443\"; ma=86400","cross-origin-resource-policy":"cross-origin","x-content-type-options":"nosniff","nel":"{\"report_to\":\"cf-nel\",\"success_fraction\":0.0,\"max_age\":604800}"},"url":"https://jsr.io/@std/semver/0.224.3/is_semver.ts","time":1785241875}