<reasoning_content>
The user wants a Python implementation of a priority-based job scheduling queue.
Requirements:
1. `add_job` method with optional `priority` parameter (default 0).
2. Higher priority jobs processed first.
3. FIFO order for same priority.
4. `get_next_job()` method returning `(str, dict)` or `None`.
5. Priority levels: 0 (Normal), 1 (High), 2 (Critical) - though the logic just needs to handle the numeric values.
6. Output only Python code.

I'll use `heapq` for priority queue functionality, but since `heapq` is a min-heap and we want higher priority first, I'll negate the priority or use a custom comparator. Actually, since we need FIFO for same priority, I can use a counter as a tie-breaker in the heap tuple.

Heap tuple structure: `(-priority, counter, job_id, job_data)`
- `-priority`: so higher priority becomes smaller negative number, popping first.
- `counter`: to maintain FIFO order.
- `job_id`: string
- `job_data`: dict

Let's implement the `JobQueue` class.

```python
import heapq

class JobQueue:
    def __init__(self):
        self._queue = []
        self._counter = 0
        
    def add_job(self, job_id: str, job_data: dict, priority: int = 0):
        heapq.heappush(self._queue, (-priority, self._counter, job_id, job_data))
        self._counter += 1
        
    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        _, _, job_id, job_data = heapq.heappop(self._queue)
        return (job_id, job_data)
```

Wait, type hints in the prompt: `get_next_job() -> tuple[str, dict] | None`. This uses Python 3.10+ union syntax. I'll stick to that or use `Optional`/`Union` if needed, but the prompt uses `|`, so I'll assume Python 3.10+.

Let's double check requirements:
- `add_job` accepts optional priority parameter (default=0) -> Done.
- Higher priority processed before lower -> Done (using negative priority).
- Same priority maintain FIFO -> Done (using counter).
- `get_next_job() -> tuple[str, dict] | None` -> Done.
- Priority levels mentioned are just examples/documentation, logic handles integers.

One minor point: `job_data` might be mutated later, but that's standard. The prompt says `tuple[str, dict]`.

I'll write the complete class.

```python
import heapq

class JobQueue:
    def __init__(self):
        self._queue = []
        self._counter = 0
        
    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._queue, (-priority, self._counter, job_id, job_data))
        self._counter += 1
        
    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        _, _, job_id, job_data = heapq.heappop(self._queue)
        return (job_id, job_data)
```

Check if any edge cases: empty queue returns None. Correct.
Type hints match prompt.
Only Python code requested.
I will output exactly the code.
One thing: `tuple[str, dict] | None` is valid in Python 3.10+. I'll keep it as is.
Should I add `from __future__ import annotations`? Not necessary if assuming 3.10+.
The prompt example uses `queue = JobQueue()`, so the class name should be `JobQueue`.
All good.
I'll produce the code block.
Wait, should I handle the priority levels explicitly? The prompt says "Priority levels: 0: Normal, 1: High, 2: Critical". It's just documentation. The logic just needs to compare integers. I'll stick to integer comparison.

One detail: `job_data` might be a dict that gets modified. That's fine.
The code looks solid.
I will output it now. 
Let's verify the exact signature requested: `get_next_job() -> tuple[str, dict] | None`
And `add_job` should accept `job_id`, `job_data`, `priority`. The example shows `queue.add_job("normal", {"type": "normal"}, priority=0)`. So positional args for id and data, keyword for priority is fine.

Code:
```python
import heapq

class JobQueue:
    def __init__(self):
        self._queue = []
        self._counter = 0
        
    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._queue, (-priority, self._counter, job_id, job_data))
        self._counter += 1
        
    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        _, _, job_id, job_data = heapq.heappop(self._queue)
        return (job_id, job_data)
```
This matches all requirements. I'll output it. 
One minor consideration: `tuple[str, dict] | None` might cause syntax error in older Python versions, but the prompt uses it, so it's fine. I'll keep it.
Ready. 
Output matches request.✅
Proceeds. 
(Self-Correction/Refinement during thought)
Wait, should I use `typing.Optional` and `typing.Tuple` for broader compatibility? The prompt uses `tuple[str, dict] | None`, so I'll stick to that.
Is there any chance `job_data` needs to be copied to avoid mutation issues? Not specified, so direct reference is fine.
The code is concise and correct.
Output matches. 
Proceeds. 
```python
import heapq

class JobQueue:
    def __init__(self):
        self._queue = []
        self._counter = 0
        
    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._queue, (-priority, self._counter, job_id, job_data))
        self._counter += 1
        
    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        _, _, job_id, job_data = heapq.heappop(self._queue)
        return (job_id, job_data)
```
All good. Output matches. 
Proceeds. 
[Output Generation] -> *Outputs the code block*
(Note: I'll make sure formatting is exact)
Done. 
(Self-Correction/Verification during generation prep)
Check requirement 4: `get_next_job() -> tuple[str, dict] | None`
Check requirement 1: `add_job` accepts optional priority (default=0)
Check requirement 2: Higher priority first -> `-priority` in heap handles this.
Check requirement 3: FIFO for same priority -> `_counter` handles this.
Example matches.
Code is ready.✅
Output matches request. 
Proceeds. 
[Final Check]
The prompt says "Write only the Python code, no explanations."
I will output exactly the code block.
All constraints met.
Done. 
`

</reasoning_content>

<content>
```python
import heapq

class JobQueue:
    def __init__(self):
        self._queue = []
        self._counter = 0
        
    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._queue, (-priority, self._counter, job_id, job_data))
        self._counter += 1
        
    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        _, _, job_id, job_data = heapq.heappop(self._queue)
        return (job_id, job_data)
```
</content>