from typing import Union
from pathlib import Path
from typing import Dict
from typing import List
from typing import Tuple
import re


def parse_rules(filename: Union[str, Path]) -> Dict[str, List[Tuple[str, int]]]:
    """
    Parse the input text into a nice formatted set of rules. We assume:
        * Each type of bag appears on the left hand side of a rule
        * The rule list does not contain (directed) cycles
        * Each bag appears only once on the left hand side of a rule

    Input format:
        [BAG TYPE] bags contain (no other bags|[N] [BAG TYPE] bags?[, [N] [BAG TYPE] bags?...]).

    Output format:
        key: Left hand side bag type
        value: List of pairs (bag type, number of bags contained in
            parent bag of this type)
    """
    rules: Dict[str, List[Tuple[str, int]]] = {}
    with open(filename, "rt") as infile:
        for line in infile:
            left, right = line.strip("\n.").split("contain")
            left = left.strip()[: -len(" bags")]
            right = right.strip()
            if right == "no other bags":
                rules[left] = []
            else:
                children = [
                    (bag_type, int(bag_num))
                    for bag_num, bag_type in re.findall(r"(\d+) ([a-z ]+) bags?", right)
                ]
                if not children:
                    raise ValueError(f"Something is wrong in regex for {right}")
                rules[left] = children

    return rules