// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

import type { Type } from "../_type.ts";
import { isNegativeZero } from "../_utils.ts";

function isCharCodeInRange(c: number, lower: number, upper: number): boolean {
  return lower <= c && c <= upper;
}

function isHexCode(c: number): boolean {
  return (
    isCharCodeInRange(c, 0x30, 0x39) || // 0-9
    isCharCodeInRange(c, 0x41, 0x46) || // A-F
    isCharCodeInRange(c, 0x61, 0x66) // a-f
  );
}

function isOctCode(c: number): boolean {
  return isCharCodeInRange(c, 0x30, 0x37); // 0-7
}

function isDecCode(c: number): boolean {
  return isCharCodeInRange(c, 0x30, 0x39); // 0-9
}

function resolveYamlInteger(data: string): boolean {
  const max = data.length;
  let index = 0;
  let hasDigits = false;

  if (!max) return false;

  let ch = data[index];

  // sign
  if (ch === "-" || ch === "+") {
    ch = data[++index];
  }

  if (ch === "0") {
    // 0
    if (index + 1 === max) return true;
    ch = data[++index];

    // base 2, base 8, base 16

    if (ch === "b") {
      // base 2
      index++;

      for (; index < max; index++) {
        ch = data[index];
        if (ch === "_") continue;
        if (ch !== "0" && ch !== "1") return false;
        hasDigits = true;
      }
      return hasDigits && ch !== "_";
    }

    if (ch === "x") {
      // base 16
      index++;

      for (; index < max; index++) {
        ch = data[index];
        if (ch === "_") continue;
        if (!isHexCode(data.charCodeAt(index))) return false;
        hasDigits = true;
      }
      return hasDigits && ch !== "_";
    }

    // base 8
    for (; index < max; index++) {
      ch = data[index];
      if (ch === "_") continue;
      if (!isOctCode(data.charCodeAt(index))) return false;
      hasDigits = true;
    }
    return hasDigits && ch !== "_";
  }

  // base 10 (except 0) or base 60

  // value should not start with `_`;
  if (ch === "_") return false;

  for (; index < max; index++) {
    ch = data[index];
    if (ch === "_") continue;
    if (!isDecCode(data.charCodeAt(index))) {
      return false;
    }
    hasDigits = true;
  }

  // Should have digits and should not end with `_`
  if (!hasDigits || ch === "_") return false;

  // base60 almost not used, no needs to optimize
  return /^(:[0-5]?[0-9])+$/.test(data.slice(index));
}

function constructYamlInteger(data: string): number {
  let value = data;

  if (value.includes("_")) {
    value = value.replace(/_/g, "");
  }

  let sign = 1;
  let ch = value[0];
  if (ch === "-" || ch === "+") {
    if (ch === "-") sign = -1;
    value = value.slice(1);
    ch = value[0];
  }

  if (value === "0") return 0;

  if (ch === "0") {
    if (value[1] === "b") return sign * parseInt(value.slice(2), 2);
    if (value[1] === "x") return sign * parseInt(value, 16);
    return sign * parseInt(value, 8);
  }

  return sign * parseInt(value, 10);
}

function isInteger(object: unknown): object is number {
  if (object instanceof Number) object = object.valueOf();
  return typeof object === "number" && object % 1 === 0 &&
    !isNegativeZero(object);
}

export const int: Type<"scalar", number> = {
  tag: "tag:yaml.org,2002:int",
  construct: constructYamlInteger,
  defaultStyle: "decimal",
  kind: "scalar",
  predicate: isInteger,
  represent: {
    // deno-lint-ignore ban-types
    binary(object: number | Number): string {
      const value = object instanceof Number ? object.valueOf() : object;
      return value >= 0
        ? `0b${value.toString(2)}`
        : `-0b${value.toString(2).slice(1)}`;
    },
    // deno-lint-ignore ban-types
    octal(object: number | Number): string {
      const value = object instanceof Number ? object.valueOf() : object;
      return value >= 0
        ? `0${value.toString(8)}`
        : `-0${value.toString(8).slice(1)}`;
    },
    // deno-lint-ignore ban-types
    decimal(object: number | Number): string {
      const value = object instanceof Number ? object.valueOf() : object;
      return value.toString(10);
    },
    // deno-lint-ignore ban-types
    hexadecimal(object: number | Number): string {
      const value = object instanceof Number ? object.valueOf() : object;
      return value >= 0
        ? `0x${value.toString(16).toUpperCase()}`
        : `-0x${value.toString(16).toUpperCase().slice(1)}`;
    },
  },
  resolve: resolveYamlInteger,
};

// denoCacheMetadata={"headers":{"age":"4275641","content-length":"4573","report-to":"{\"group\":\"cf-nel\",\"max_age\":604800,\"endpoints\":[{\"url\":\"https://a.nel.cloudflare.com/report/v4?s=vNjJ%2FP4ryAaRb8jER%2FMirwvTCe4e8EazDXu7IIn7KHfMmXnak7uQGC9LC7AgsMjmRQsKwzpmgTK3zkMtKzM09i8nzkJnrKgOEp3ttGfvXGqRB3CUrAYRVpI%3D\"}]}","server":"cloudflare","content-type":"text/typescript","access-control-allow-origin":"*","cache-control":"public, max-age=31536000, immutable","date":"Tue, 28 Jul 2026 12:31:15 GMT","alt-svc":"h3=\":443\"; ma=86400","set-cookie":"__cf_bm=RYnUgf_4dPSB3ELhyjqN4nIwZv_gAE86f9k9BjtBlJI-1785241875.3696284-1.0.1.1-MyAvZFSHLW0ni4dr7GtiB9.Fn6axpwyCGczB6U_0br3gQOUy6TpGJXtdv3UixcBsj1UrzxVhRdiLWDgmuXQuO0YijdKkFEuaudI.JMqdxCP37gwH0BHF4qz8Dhnk29Sl; HttpOnly; SameSite=None; Secure; Path=/; Domain=jsr.io; Expires=Tue, 28 Jul 2026 13:01:15 GMT","vary":"Origin","cf-cache-status":"HIT","nel":"{\"report_to\":\"cf-nel\",\"success_fraction\":0.0,\"max_age\":604800}","x-content-type-options":"nosniff","cf-ray":"a223e6590e407db6-LAX","accept-ranges":"bytes","x-robots-tag":"noindex","cross-origin-resource-policy":"cross-origin","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';","etag":"\"6b2ad0f5af3dd219958289832a6eeecb\"","x-jsr-backend":"modules","access-control-expose-headers":"*"},"url":"https://jsr.io/@std/yaml/1.0.5/_type/int.ts","time":1785241875}