// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

import { isPosixPathSeparator } from "./_util.ts";
import { resolve } from "./resolve.ts";
import { assertArgs } from "../_common/relative.ts";

/**
 * Return the relative path from `from` to `to` based on current working directory.
 *
 * If `from` and `to` are the same, return an empty string.
 *
 * @example Usage
 * ```ts
 * import { relative } from "@std/path/posix/relative";
 * import { assertEquals } from "@std/assert/assert-equals";
 *
 * const path = relative("/data/orandea/test/aaa", "/data/orandea/impl/bbb");
 * assertEquals(path, "../../impl/bbb");
 * ```
 *
 * @param from The path to start from.
 * @param to The path to reach.
 * @returns The relative path.
 */
export function relative(from: string, to: string): string {
  assertArgs(from, to);

  from = resolve(from);
  to = resolve(to);

  if (from === to) return "";

  // Trim any leading backslashes
  let fromStart = 1;
  const fromEnd = from.length;
  for (; fromStart < fromEnd; ++fromStart) {
    if (!isPosixPathSeparator(from.charCodeAt(fromStart))) break;
  }
  const fromLen = fromEnd - fromStart;

  // Trim any leading backslashes
  let toStart = 1;
  const toEnd = to.length;
  for (; toStart < toEnd; ++toStart) {
    if (!isPosixPathSeparator(to.charCodeAt(toStart))) break;
  }
  const toLen = toEnd - toStart;

  // Compare paths to find the longest common path from root
  const length = fromLen < toLen ? fromLen : toLen;
  let lastCommonSep = -1;
  let i = 0;
  for (; i <= length; ++i) {
    if (i === length) {
      if (toLen > length) {
        if (isPosixPathSeparator(to.charCodeAt(toStart + i))) {
          // We get here if `from` is the exact base path for `to`.
          // For example: from='/foo/bar'; to='/foo/bar/baz'
          return to.slice(toStart + i + 1);
        } else if (i === 0) {
          // We get here if `from` is the root
          // For example: from='/'; to='/foo'
          return to.slice(toStart + i);
        }
      } else if (fromLen > length) {
        if (isPosixPathSeparator(from.charCodeAt(fromStart + i))) {
          // We get here if `to` is the exact base path for `from`.
          // For example: from='/foo/bar/baz'; to='/foo/bar'
          lastCommonSep = i;
        } else if (i === 0) {
          // We get here if `to` is the root.
          // For example: from='/foo'; to='/'
          lastCommonSep = 0;
        }
      }
      break;
    }
    const fromCode = from.charCodeAt(fromStart + i);
    const toCode = to.charCodeAt(toStart + i);
    if (fromCode !== toCode) break;
    else if (isPosixPathSeparator(fromCode)) lastCommonSep = i;
  }

  let out = "";
  // Generate the relative path based on the path difference between `to`
  // and `from`
  for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
    if (i === fromEnd || isPosixPathSeparator(from.charCodeAt(i))) {
      if (out.length === 0) out += "..";
      else out += "/..";
    }
  }

  // Lastly, append the rest of the destination (`to`) path that comes after
  // the common path parts
  if (out.length > 0) return out + to.slice(toStart + lastCommonSep);
  else {
    toStart += lastCommonSep;
    if (isPosixPathSeparator(to.charCodeAt(toStart))) ++toStart;
    return to.slice(toStart);
  }
}

// denoCacheMetadata={"headers":{"set-cookie":"__cf_bm=8gkw4.r72K9H_CpAVeagDIl75iwLoOiH6vVxek2csj0-1785241875.3449454-1.0.1.1-wqnKqJMjgMYLZrm_Rkyz.QuSU92nR39embY7sg7HbqNUnPJtXu81BJ2uXIlCR_f5vwV6um9vvlEyZelnBhRQI.SafeNeid08C8BCQkwbZTqsmG6BvNIKAzdn1SvI38OX; 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=a587LpRwT2D6GVOKeWLAj%2BYw2y7sOE%2Fcom%2BI3N%2BeZutpT5wlkiHdE1EbnUYDpfEvgbdbuSBkBirO2AMABVnLEEeEqz4LJ2DfE1PlRCSNQ1YUC%2BHE3J4tUy4%3D\"}]}","nel":"{\"report_to\":\"cf-nel\",\"success_fraction\":0.0,\"max_age\":604800}","cross-origin-resource-policy":"cross-origin","content-type":"text/typescript","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","x-jsr-backend":"modules","content-length":"3345","accept-ranges":"bytes","alt-svc":"h3=\":443\"; ma=86400","vary":"Origin","cache-control":"public, max-age=31536000, immutable","cf-cache-status":"HIT","date":"Tue, 28 Jul 2026 12:31:15 GMT","access-control-allow-origin":"*","x-robots-tag":"noindex","cf-ray":"a223e658cdb97db6-LAX","age":"38659","x-content-type-options":"nosniff","etag":"\"b92cb84d2358aba42e673e8913764851\"","access-control-expose-headers":"*"},"url":"https://jsr.io/@std/path/1.0.0-rc.2/posix/relative.ts","time":1785241875}