from typing import Optional
from typing import BinaryIO
import io
import tarfile


def merge_tar_gz(a: Optional[BinaryIO], b: Optional[BinaryIO]) -> Optional[BinaryIO]:
    """Merge <a> and <b>, returning a new tarfile.TarFile object.

    If two files in <a> and <b> have the same name, the one in <a> prevails.
    
    Both a and b can be safely closed after this function.
    
    Returns
        None       - If both arguments are None.
        a          - If <b> is None.
        b          - If <a> is None.
        io.BytesIO - A BytesIO, cursor set at the start, corresponding to the
                     merging of <a> into <b> (overwriting file with the same
                     name)."""
    if a is None:
        return None if b is None else io.BytesIO(b.read())
    if b is None:
        return io.BytesIO(a.read())
    
    destio = io.BytesIO()
    
    with tarfile.open(fileobj=a, mode="r:gz") as t1, \
            tarfile.open(fileobj=b, mode="r:gz") as t2, \
            tarfile.open(fileobj=destio, mode="w:gz") as dest:
        
        t1_members = [m for m in t1.getmembers()]
        t1_names = t1.getnames()
        t2_members = [m for m in t2.getmembers() if m.name not in t1_names]
        
        for member in t1_members:
            if member.isdir():
                dest.addfile(member)
            else:
                dest.addfile(member, t1.extractfile(member))
        
        for member in t2_members:
            if member.isdir():
                dest.addfile(member)
            else:
                dest.addfile(member, t2.extractfile(member))
    
    destio.seek(0)
    return destio