from typing import Optional
from pathlib import Path


def to_uri(
    path: str, local_basedir: Optional[Path] = None, relative_prefix: str = ""
) -> str:
    """
    Given a path or URI, normalize it to an absolute path.

    If the path is relative and without protocol, it is prefixed with `relative_prefix`
    before attempting to resolve it (by default equal to prepending `cwd://`)

    If path is already http(s):// or file://... path, do nothing to it.
    If the path is absolute (starts with a slash), just prepend file://
    If the path is cwd://, resolve based on CWD (even if starting with a slash)
    If the path is local://, resolve based on `local_basedir` (if not given, CWD is used)

    Result is either http(s):// or a file:// path that can be read with urlopen.
    """
    local_basedir = local_basedir or Path("")
    if str(path)[0] != "/" and str(path).find("://") < 0:
        path = relative_prefix + path

    prot, rest = "", ""
    prs = str(path).split("://")
    if len(prs) == 1:
        rest = prs[0]
    else:
        prot, rest = prs

    if prot.startswith(("http", "file")):
        return path  # nothing to do
    elif prot == "local":
        # relative, but not to CWD, but a custom path
        rest = str((local_basedir / rest.lstrip("/")).absolute())
    elif prot == "cwd":
        # like normal resolution of relative,
        # but absolute paths are still interpreted relative,
        # so cwd:// and cwd:/// are lead to the same results
        rest = str((Path(rest.lstrip("/"))).absolute())
    elif prot == "":
        # relative paths are made absolute
        if not Path(rest).is_absolute():
            rest = str((Path(rest)).absolute())
    else:
        raise ValueError(f"Unknown protocol: {prot}")

    return f"file://{rest}"