// 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 { Schema, SchemaDefinition } from "../schema.ts";
import { State } from "../_state.ts";
import type { StyleVariant, Type } from "../type.ts";
import type { Any, ArrayObject } from "../_utils.ts";

const { hasOwn } = Object;

function compileStyleMap(
  schema: Schema,
  map?: ArrayObject<StyleVariant> | null,
): ArrayObject<StyleVariant> {
  if (typeof map === "undefined" || map === null) return {};

  const result: ArrayObject<StyleVariant> = {};
  for (let tag of Object.keys(map)) {
    let style = String(map[tag]) as StyleVariant;
    if (tag.slice(0, 2) === "!!") {
      tag = `tag:yaml.org,2002:${tag.slice(2)}`;
    }
    const type = schema.compiledTypeMap.fallback[tag];

    if (
      type &&
      typeof type.styleAliases !== "undefined" &&
      hasOwn(type.styleAliases, style)
    ) {
      style = type.styleAliases[style];
    }

    result[tag] = style;
  }

  return result;
}

export interface DumperStateOptions {
  /** indentation width to use (in spaces). */
  indent?: number;
  /** when true, will not add an indentation level to array elements */
  noArrayIndent?: boolean;
  /**
   * do not throw on invalid types (like function in the safe schema)
   * and skip pairs and single values with such types.
   */
  skipInvalid?: boolean;
  /**
   * specifies level of nesting, when to switch from
   * block to flow style for collections. -1 means block style everywhere
   */
  flowLevel?: number;
  /** Each tag may have own set of styles.	- "tag" => "style" map. */
  styles?: ArrayObject<StyleVariant> | null;
  /** specifies a schema to use. */
  schema?: SchemaDefinition;
  /**
   * If true, sort keys when dumping YAML in ascending, ASCII character order.
   * If a function, use the function to sort the keys. (default: false)
   * If a function is specified, the function must return a negative value
   * if first argument is less than second argument, zero if they're equal
   * and a positive value otherwise.
   */
  sortKeys?: boolean | ((a: string, b: string) => number);
  /** set max line width. (default: 80) */
  lineWidth?: number;
  /**
   * if true, don't convert duplicate objects
   * into references (default: false)
   */
  noRefs?: boolean;
  /**
   * if true don't try to be compatible with older yaml versions.
   * Currently: don't quote "yes", "no" and so on,
   * as required for YAML 1.1 (default: false)
   */
  noCompatMode?: boolean;
  /**
   * if true flow sequences will be condensed, omitting the
   * space between `key: value` or `a, b`. Eg. `'[a,b]'` or `{a:{b:c}}`.
   * Can be useful when using yaml for pretty URL query params
   * as spaces are %-encoded. (default: false).
   */
  condenseFlow?: boolean;
}

export class DumperState extends State {
  indent: number;
  noArrayIndent: boolean;
  skipInvalid: boolean;
  flowLevel: number;
  sortKeys: boolean | ((a: Any, b: Any) => number);
  lineWidth: number;
  noRefs: boolean;
  noCompatMode: boolean;
  condenseFlow: boolean;
  implicitTypes: Type[];
  explicitTypes: Type[];
  tag: string | null = null;
  result = "";
  duplicates: Any[] = [];
  usedDuplicates: Any[] = []; // changed from null to []
  styleMap: ArrayObject<StyleVariant>;
  dump: Any;

  constructor({
    schema,
    indent = 2,
    noArrayIndent = false,
    skipInvalid = false,
    flowLevel = -1,
    styles = null,
    sortKeys = false,
    lineWidth = 80,
    noRefs = false,
    noCompatMode = false,
    condenseFlow = false,
  }: DumperStateOptions) {
    super(schema);
    this.indent = Math.max(1, indent);
    this.noArrayIndent = noArrayIndent;
    this.skipInvalid = skipInvalid;
    this.flowLevel = flowLevel;
    this.styleMap = compileStyleMap(this.schema as Schema, styles);
    this.sortKeys = sortKeys;
    this.lineWidth = lineWidth;
    this.noRefs = noRefs;
    this.noCompatMode = noCompatMode;
    this.condenseFlow = condenseFlow;

    this.implicitTypes = (this.schema as Schema).compiledImplicit;
    this.explicitTypes = (this.schema as Schema).compiledExplicit;
  }
}

// denoCacheMetadata={"headers":{"accept-ranges":"bytes","content-type":"text/typescript","x-jsr-backend":"modules","access-control-expose-headers":"*","age":"1643671","x-content-type-options":"nosniff","etag":"\"270572f39bea9c98433372fe2cfb14f8\"","cf-ray":"a223e6586c677db6-LAX","cache-control":"public, max-age=31536000, immutable","vary":"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';","server":"cloudflare","cf-cache-status":"HIT","date":"Tue, 28 Jul 2026 12:31:15 GMT","nel":"{\"report_to\":\"cf-nel\",\"success_fraction\":0.0,\"max_age\":604800}","content-length":"4284","report-to":"{\"group\":\"cf-nel\",\"max_age\":604800,\"endpoints\":[{\"url\":\"https://a.nel.cloudflare.com/report/v4?s=uxoKPX04qRr9P9feCGqi0OWZ0Tt2CBKnyKT8WGXbSad3z5pldtkwxqtFS%2Ff%2FydJ%2BSgRSmCjrsiO%2FgFI8LBAcBUD%2F3upOeRiKn0aadm11uJWZhTKaKikl3%2B4%3D\"}]}","set-cookie":"__cf_bm=Hwn7z.YYaQ56odlRvGh2BlZPwMcO4bXCAiz9UsHuicE-1785241875.2724774-1.0.1.1-gdsjY_bNmTpEJaBh92iRfcyT1G3D4owShzbhyHWfEyFNO2TqXx4IBYNTVuPI_LozwowKbrOgoSY7zWvObAhrzkm8YWgXc17I4BcsSsRCxcX5pM_dPHi9lwjy4s4Hr3p.; HttpOnly; SameSite=None; Secure; Path=/; Domain=jsr.io; Expires=Tue, 28 Jul 2026 13:01:15 GMT","x-robots-tag":"noindex","cross-origin-resource-policy":"cross-origin","alt-svc":"h3=\":443\"; ma=86400","access-control-allow-origin":"*"},"url":"https://jsr.io/@std/yaml/0.224.3/_dumper/dumper_state.ts","time":1785241875}