```python
from typing import *
from collections import *

def topo_sort(n: int, edges: list[tuple[int, int]]) -> list[int]:
    if n == 0:
        return []

    # Build adjacency list representation of the graph
    adj_list = [[] for _ in range(n)]
    indegree = [0] * n

    for a, b in edges:
        adj_list[a].append(b)
        indegree[b] += 1

    # Initialize queue with nodes having no incoming edges
    queue = deque([i for i in range(n) if indegree[i] == 0])

    topo_order = []
    while queue:
        node = queue.popleft()
        topo_order.append(node)

        # Decrease the indegree of all adjacent nodes
        for neighbor in adj_list[node]:
            indegree[neighbor] -= 1

            # If a neighbor's indegree becomes zero, add it to the queue
            if indegree[neighbor] == 0:
                queue.append(neighbor)

    # Check if all nodes were processed
    if len(topo_order) != n:
        raise ValueError("Graph contains a cycle")

    return topo_order
```