"""Helpers related to parsing"""

import os
import re
from typing import Dict, Tuple, Union
from urllib.parse import ParseResult, urlparse


def parse_size(s: str) -> Union[int, str]:
    """Parse a size string into a number or 'unlimited'.

    Supported suffixes: kB, kiB, MB, MiB, GB, GiB, TB, TiB
    """
    units = [
        (r'^\s*(\d+)\s*kB$', 1000, 1),
        (r'^\s*(\d+)\s*KiB$', 1024, 1),
        (r'^\s*(\d+)\s*MB$', 1000, 2),
        (r'^\s*(\d+)\s*MiB$', 1024, 2),
        (r'^\s*(\d+)\s*GB$', 1000, 3),
        (r'^\s*(\d+)\s*GiB$', 1024, 3),
        (r'^\s*(\d+)\s*TB$', 1000, 4),
        (r'^\s*(\d+)\s*TiB$', 1024, 4),
        (r'^\s*(\d+)$', 1, 1),
        (r'^unlimited$', "unlimited", 1),
    ]

    for pat, base, power in units:
        m = re.fullmatch(pat, s)
        if m:
            if isinstance(base, int):
                return int(m.group(1)) * base ** power
            if base == "unlimited":
                return "unlimited"

    raise TypeError(f"invalid size value: '{s}'")


def find_mount_root(url: ParseResult, args: Dict) -> os.PathLike:
    """
    Parses the mount URL to extract the root path.

    Parameters:
    - url (ParseResult): The ParseResult object obtained from urlparse.
    - args (Dict):A dictionary containing arguments including mounts and
    path information as passed by osbuild.api.arguments()
    """
    name = url.netloc
    if name:
        root = args["mounts"].get(name, {}).get("path")
        if root is None:
            raise ValueError(f"Unknown mount '{name}'")
    else:
        root = args["paths"]["mounts"]

    return root


def parse_input(url: ParseResult, args: Dict) -> os.PathLike:
    """
    Parses the input URL to extract the root path.

    Parameters:
    - url (ParseResult): The ParseResult object obtained from urlparse.
    - args (Dict): A dictionary containing arguments including mounts and
    path information as passed by osbuild.api.arguments()
    """
    name = url.netloc
    root = args["inputs"].get(name, {}).get("path")
    if root is None:
        raise ValueError(f"Unknown input '{name}'")

    return root


def parse_location_into_parts(location: str, args: Dict) -> Tuple[str, str]:
    """
    Parses the location URL to derive the corresponding root and url path.

    Parameters:
    - location (str): The location URL to be parsed. If the URL has no scheme,
    then 'tree://' is implied
    - args (Dict): A dictionary containing arguments including mounts and
    path information as passed by osbuild.api.arguments()
    """

    if "://" not in location:
        location = f"tree://{location}"

    url = urlparse(location)

    scheme = url.scheme
    if scheme == "tree":
        root = args["tree"]
    elif scheme == "mount":
        root = find_mount_root(url, args)
    elif scheme == "input":
        root = parse_input(url, args)
    else:
        raise ValueError(f"Unsupported scheme '{scheme}'")

    if not url.path.startswith("/"):
        raise ValueError(f"url.path from location must start with '/', got: {url.path}")

    return root, url.path


def parse_location(location: str, args: Dict) -> str:
    """
    Parses the location URL to derive the corresponding file path.

    Parameters:
    - location (str): The location URL to be parsed.
    - args (Dict): A dictionary containing arguments including mounts and
    path information as passed by osbuild.api.arguments()
    """

    root, urlpath = parse_location_into_parts(location, args)
    path = os.path.relpath(urlpath, "/")
    path = os.path.join(root, path)
    path = os.path.normpath(path)
    if urlpath.endswith("/"):
        path = os.path.join(path, ".")

    return path
