<reasoning_content>
The user wants a Python implementation of a priority-based job scheduling queue.
Requirements:
1. Class `JobQueue`
2. Method `add_job(job_id: str, job_data: dict, priority: int = 0)`
3. Higher priority values are processed first.
4. FIFO for same priority.
5. Method `get_next_job() -> tuple[str, dict] | None`
6. Priority levels: 0=Normal, 1=High, 2=Critical (just informational, but logic should handle higher numbers first).
7. Output only Python code.

Implementation details:
- Use `heapq` for priority queue, or a sorted list. Since we need FIFO for same priority, we can use a counter as a tie-breaker.
- Alternatively, use `collections.deque` for each priority level, but `heapq` is more standard for priority queues.
- With `heapq`, we store tuples: `(-priority, counter, job_id, job_data)`. Negative priority because `heapq` is a min-heap, and we want higher priority first.
- Counter ensures FIFO for same priority.

Let's draft the class:

```python
import heapq
from typing import Tuple, Dict, Optional

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        # Negate priority because heapq is a min-heap
        heapq.heappush(self._queue, (-priority, self._counter, job_id, job_data))
        self._counter += 1

    def get_next_job(self) -> Optional[Tuple[str, dict]]:
        if not self._queue:
            return None
        _, _, job_id, job_data = heapq.heappop(self._queue)
        return (job_id, job_data)
```

Check requirements:
1. `add_job` accepts optional priority parameter (default=0) - Yes.
2. Higher priority jobs processed before lower - Yes, using `-priority`.
3. Same priority maintains FIFO - Yes, using `_counter`.
4. `get_next_job() -> tuple[str, dict] | None` - Yes.
5. Priority levels are just for context, logic handles integers.
6. Example matches usage.

One minor thing: The return type hint in Python 3.9+ supports `tuple[str, dict] | None`, but to be safe across versions, `Optional[Tuple[str, dict]]` is fine, or just use the union syntax if assuming modern Python. The prompt uses `tuple[str, dict] | None`, so I'll stick to that or import `Tuple` and `Optional`. I'll use standard typing imports for compatibility, or just rely on Python 3.10+ syntax as requested. I'll use `from typing import Tuple, Optional` to be safe, or just write it as requested.

Actually, the prompt says `get_next_job() -> tuple[str, dict] | None`. I'll use that exact signature.

Let's refine:

```python
import heapq
from typing import Tuple, Optional

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)
```

Wait, `Tuple[str, dict] | None` syntax requires Python 3.10+. That's fine. I'll stick to it.
Alternatively, I can just use `typing.Optional` and `typing.Tuple` to be safe, but the prompt explicitly used the union syntax. I'll follow the prompt's style.

One thing: `job_data` is a dict. In Python, `dict` is fine.
I'll make sure the code is clean and matches requirements exactly.

Check edge cases:
- Empty queue returns None.
- Same priority preserves order.
- Higher priority comes out first.
All good.

I will output only the Python code as requested.
No extra text.
Ready. 
Let's double check the exact signature requested: `get_next_job() -> tuple[str, dict] | None`
I'll use that.

One minor point: `heapq` stores tuples. When priorities are equal, it compares the second element (`_counter`), which is unique and increasing, so FIFO is preserved. The third element is `job_id` (string), which is comparable, but since `_counter` is unique, it won't be reached unless `_counter` collides (which it won't). So it's safe.

Code:
```python
import heapq
from typing import Tuple, Optional

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)
```

Wait, `Tuple[str, dict] | None` might cause a syntax error in Python < 3.10 if `from __future__ import annotations` isn't used, but it's standard now. I'll just use `Optional[Tuple[str, dict]]` to be strictly compatible, or keep the prompt's syntax. The prompt uses `tuple[str, dict] | None`, so I'll stick to that. Actually, Python 3.9+ supports `tuple[str, dict]` but not `|` until 3.10. I'll assume modern Python. To be perfectly safe, I'll use `from typing import Tuple, Optional` and `Optional[Tuple[str, dict]]`, but the prompt explicitly wrote `tuple[str, dict] | None`. I'll just write it exactly as requested.

Actually, I'll just write it without imports if possible, but `heapq` needs import. I'll include imports.

Final code:
```python
import heapq
from typing import Tuple, Optional

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)
```
Wait, `Tuple[str, dict] | None` is valid in 3.10+. I'll keep it.
All requirements met.
Output matches request.
Proceed. 
One thing: `job_data` might be mutated by the caller, but that's outside scope. The requirement says `job_data: dict`.
I'll output exactly the code.
Done. 
Let's produce it. 
Self-Correction/Verification during thought:
Is `heapq` necessary? Could use `sorted` list, but `heapq` is O(log n). It's standard.
The tie-breaker `_counter` ensures FIFO for same priority.
`-priority` ensures higher priority comes first in min-heap.
Return type matches.
Default priority=0 matches.
Code is concise.
Ready. 
Output matches the final code block.✅
Proceeds. 
[Output Generation] -> *Python code only*
```python
import heapq
from typing import Tuple, Optional

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)
```
Wait, `Tuple[str, dict] | None` might be better written as `Optional[Tuple[str, dict]]` for broader compatibility, but I'll stick to the prompt's syntax. Actually, Python 3.10+ is standard now. I'll keep it.
All good. 
Output matches. 
Proceeds. 
[Final Check]
- `add_job` accepts optional priority=0? Yes.
- Higher priority processed first? Yes (`-priority`).
- Same priority FIFO? Yes (`_counter`).
- `get_next_job()` returns `tuple[str, dict] | None`? Yes.
- Only Python code? Yes.
Done. 
Output Generation. 
(Self-Correction/Note during generation prep)
I'll remove the `from typing import Tuple, Optional` if I use the `|` syntax directly, but it's harmless. I'll keep imports clean.
Actually, `tuple[str, dict] | None` is fine.
I will output exactly the code block.
Done. 
[Output] -> *Proceeds*

</reasoning_content>

<content>
```python
import heapq
from typing import Tuple, Optional

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>