// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

type LineParseResult = {
  key: string;
  unquoted: string;
  interpolated: string;
  notInterpolated: string;
};

type CharactersMap = { [key: string]: string };

const RE_KEY_VALUE =
  /^\s*(?:export\s+)?(?<key>[a-zA-Z_]+[a-zA-Z0-9_]*?)\s*=[\ \t]*('\n?(?<notInterpolated>(.|\n)*?)\n?'|"\n?(?<interpolated>(.|\n)*?)\n?"|(?<unquoted>[^\n#]*)) *#*.*$/gm;

const RE_EXPAND_VALUE =
  /(\${(?<inBrackets>.+?)(\:-(?<inBracketsDefault>.+))?}|(?<!\\)\$(?<notInBrackets>\w+)(\:-(?<notInBracketsDefault>.+))?)/g;

function expandCharacters(str: string): string {
  const charactersMap: CharactersMap = {
    "\\n": "\n",
    "\\r": "\r",
    "\\t": "\t",
  };

  return str.replace(
    /\\([nrt])/g,
    ($1: keyof CharactersMap): string => charactersMap[$1] || "",
  );
}

function expand(str: string, variablesMap: { [key: string]: string }): string {
  if (RE_EXPAND_VALUE.test(str)) {
    return expand(
      str.replace(RE_EXPAND_VALUE, function (...params) {
        const {
          inBrackets,
          inBracketsDefault,
          notInBrackets,
          notInBracketsDefault,
        } = params[params.length - 1];
        const expandValue = inBrackets || notInBrackets;
        const defaultValue = inBracketsDefault || notInBracketsDefault;

        let value: string | undefined = variablesMap[expandValue];
        if (value === undefined) {
          value = Deno.env.get(expandValue);
        }
        return value === undefined ? expand(defaultValue, variablesMap) : value;
      }),
      variablesMap,
    );
  } else {
    return str;
  }
}

/**
 * Parse `.env` file output in an object.
 *
 * @example Usage
 * ```ts
 * import { parse } from "@std/dotenv/parse";
 * import { assertEquals } from "@std/assert";
 *
 * const env = parse("GREETING=hello world");
 * assertEquals(env, { GREETING: "hello world" });
 * ```
 *
 * @param text The text to parse.
 * @returns The parsed object.
 */
export function parse(text: string): Record<string, string> {
  const env: Record<string, string> = {};

  let match;
  const keysForExpandCheck = [];

  while ((match = RE_KEY_VALUE.exec(text)) !== null) {
    const { key, interpolated, notInterpolated, unquoted } = match
      ?.groups as LineParseResult;

    if (unquoted) {
      keysForExpandCheck.push(key);
    }

    env[key] = typeof notInterpolated === "string"
      ? notInterpolated
      : typeof interpolated === "string"
      ? expandCharacters(interpolated)
      : unquoted.trim();
  }

  //https://github.com/motdotla/dotenv-expand/blob/ed5fea5bf517a09fd743ce2c63150e88c8a5f6d1/lib/main.js#L23
  const variablesMap = { ...env };
  keysForExpandCheck.forEach((key) => {
    env[key] = expand(env[key]!, variablesMap);
  });

  return env;
}

// denoCacheMetadata={"headers":{"x-jsr-backend":"modules","access-control-allow-origin":"*","set-cookie":"__cf_bm=YCiWS81UCZFRm5kGIpdHGz8Po8o0k0tMM4iw9i.j2V4-1785241874.9728353-1.0.1.1-0OSqK1CkxNkuUeCYlplJ8NKvZDD4JqQ7SLA5qrKkQI4jF7lMjYJAB3sB72gjNNwtfTK2lyoYf1cdUvY8lh2eucJf_hkgg.ldSPUhcPgb4SUOI0tiI84Z3tAheoRgcniK; HttpOnly; SameSite=None; Secure; Path=/; Domain=jsr.io; Expires=Tue, 28 Jul 2026 13:01:14 GMT","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';","x-content-type-options":"nosniff","report-to":"{\"group\":\"cf-nel\",\"max_age\":604800,\"endpoints\":[{\"url\":\"https://a.nel.cloudflare.com/report/v4?s=KvpkKKTBkRzlcksrmJMkbMEl2SB1aJazDnY3GsmDfYHDISOvGwH9%2F6Hbxja80AbFMowEmq7HL0bd2VNqgh0YCnypDagRMYGGPfP9B%2Bc%2FRQfDLl3%2FMDxzWdc%3D\"}]}","etag":"\"540a6080d76a970d157d08e50a727823\"","accept-ranges":"bytes","vary":"Origin","access-control-expose-headers":"*","age":"864831","content-length":"2796","cf-ray":"a223e6564f657db6-LAX","cache-control":"public, max-age=31536000, immutable","cross-origin-resource-policy":"cross-origin","date":"Tue, 28 Jul 2026 12:31:14 GMT","server":"cloudflare","x-robots-tag":"noindex","content-type":"text/typescript","cf-cache-status":"HIT","nel":"{\"report_to\":\"cf-nel\",\"success_fraction\":0.0,\"max_age\":604800}","alt-svc":"h3=\":443\"; ma=86400"},"url":"https://jsr.io/@std/dotenv/0.224.2/parse.ts","time":1785241874}