<reasoning_content>
*   Goal: Implement a priority-based job queue.
    *   Priority rules:
        *   Higher value = Higher priority (Critical = 2, High = 1, Normal = 0).
        *   Higher priority jobs go first.
        *   Same priority -> FIFO (First-In, First-Out).
    *   Methods needed:
        *   `add_job(name: str, data: dict, priority: int = 0)`
        *   `get_next_job() -> tuple[str, dict] | None`
    *   Constraints: Write *only* Python code.

    *   `heapq` (priority queue) is the standard way in Python to handle priority.
    *   `heapq` is a min-heap by default.
    *   To make it a max-heap (where higher numbers come first), we can store priorities as negative values.
    *   To handle FIFO for same priority: we need a counter/timestamp to act as a secondary sort key.

    *   Heap elements will be tuples: `(-priority, sequence_number, job_name, job_data)`.
    *   `sequence_number` ensures FIFO for same priority because `heapq` compares tuple elements lexicographically.
    *   `sequence_number` increments with every `add_job`.

    ```python
    import heapq
    from itertools import count

    class JobQueue:
        def __init__(self):
            self._queue = []
            self._counter = count()

        def add_job(self, name: str, data: dict, priority: int = 0):
            # We negate priority because heapq is a min-heap.
            # -2 (Critical) < -1 (High) < 0 (Normal)
            # But wait, the prompt says "Higher priority jobs should be processed before lower priority jobs".
            # If priority=2 is Critical and 0 is Normal, we want 2 to come out first.
            # In a min-heap, the smallest element is popped.
            # So if we push (-2, 1, ...), (-1, 2, ...), (0, 3, ...), the heap pops -2 first.
            # This is correct.
            count_val = next(self._counter)
            heapq.heappush(self._queue, (-priority, count_val, name, data))

        def get_next_job(self) -> tuple[str, dict] | None:
            if not self._queue:
                return None
            _, _, name, data = heapq.heappop(self._queue)
            return name, data
    ```

    *   `add_job("normal", ..., priority=0)` -> `(0, 0, "normal", ...)`
    *   `add_job("critical", ..., priority=2)` -> `(-2, 1, "critical", ...)`
    *   `add_job("high", ..., priority=1)` -> `(-1, 2, "high", ...)`
    *   Heap pops: `(-2, 1, ...)` then `(-1, 2, ...)` then `(0, 3, ...)`.
    *   Resulting order: critical, high, normal.
    *   Matches requirements.

    *   Check method signatures against requirements.
    *   `add_job(self, name: str, data: dict, priority: int = 0)` - OK.
    *   `get_next_job(self) -> tuple[str, dict] | None` - OK.

    *   Wait, requirement 3: "Jobs with the same priority should maintain FIFO order".
    *   If two jobs have priority 2:
        *   Job A: `(-2, 5, nameA, dataA)`
        *   Job B: `(-2, 6, nameB, dataB)`
        *   Min-heap pops `(-2, 5, ...)` first. This is FIFO. OK.

    *   Requirement 1: `add_job` accepts optional priority (default 0). OK.
    *   Requirement 2: Higher priority before lower. OK.
    *   Requirement 3: Same priority FIFO. OK.
    *   Requirement 4: `get_next_job` signature. OK.

    *   Inputting code block only.

    (Self-Correction on Type Hinting):
    `tuple[str, dict] | None` requires Python 3.10+. I will assume modern Python environment.

    ```python
    import heapq
    from itertools import count

    class JobQueue:
        def __init__(self):
            self._queue = []
            self._counter = count()

        def add_job(self, name: str, data: dict, priority: int = 0) -> None:
            # Using -priority because heapq is a min-heap, 
            # and we want higher priority numbers to be popped first.
            # The counter ensures FIFO for items with the same priority.
            heapq.heappush(self._queue, (-priority, next(self._counter), name, data))

        def get_next_job(self) -> tuple[str, dict] | None:
            if not self._queue:
                return None
            _, _, name, data = heapq.heappop(self._queue)
            return name, data
    ```

    One final check on `get_next_job`:
    If `_queue` is empty, return `None`. Correct.
    Return type `tuple[str, dict] | None`. Correct.

    Final Code Structure:
    ```python
    import heapq
    from itertools import count

    class JobQueue:
        def __init__(self):
            self._queue = []
            self._counter = count()

        def add_job(self, name: str, data: dict, priority: int = 0) -> None:
            heapq.heappush(self._queue, (-priority, next(self._counter), name, data))

        def get_next_job(self) -> tuple[str, dict] | None:
            if not self._queue:
                return None
            priority_neg, sequence, name, data = heapq.heappop(self._queue)
            return name, data
    ```

    This looks solid.
</reasoning_content>

<content>
```python
import heapq
from itertools import count

class JobQueue:
    def __init__(self):
        self._queue = []
        self._counter = count()

    def add_job(self, name: str, data: dict, priority: int = 0) -> None:
        # We negate priority because heapq is a min-heap, but we want 
        # higher priority values to be processed first.
        # The counter ensures FIFO order for jobs with the same priority.
        heapq.heappush(self._queue, (-priority, next(self._counter), name, data))

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        
        _, _, name, data = heapq.heappop(self._queue)
        return name, data
```
</content>