<reasoning_content>
The user wants a priority-based job scheduling queue implemented in Python.
Requirements:
1. `add_job` accepts an optional `priority` parameter (default=0).
2. Higher priority jobs are processed first.
3. FIFO order for jobs with the same priority.
4. `get_next_job()` returns a tuple `(str, dict)` or `None`.
5. Priority levels: 0 (Normal), 1 (High), 2 (Critical).
6. Return only Python code, no explanations.

Implementation approach:
- Use a priority queue. `heapq` module in Python is suitable.
- Since higher priority should be processed first, and `heapq` is a min-heap, we can negate the priority or use a max-heap approach. Alternatively, store `(-priority, timestamp, job_id, job_data)` in the heap.
- To maintain FIFO for same priority, we need a counter/timestamp to break ties.
- `add_job(self, job_id: str, job_data: dict, priority: int = 0)`: push to heap.
- `get_next_job(self) -> tuple[str, dict] | None`: pop from heap if not empty.

Let's refine the heap structure:
Each element in the heap will be a tuple: `(-priority, counter, job_id, job_data)`
`counter` ensures FIFO order for same priority. Since `heapq` pops the smallest, `-priority` ensures higher priority comes first. `counter` ensures earlier jobs come first for same priority.

Wait, the requirement says "Higher priority jobs should be processed before lower priority jobs". Priority 2 > 1 > 0.
So `-priority` works: -2 < -1 < 0. Min-heap pops -2 first. Correct.

Data structures:
```python
import heapq
from typing import Optional, Tuple

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, job_id, job_data))
        self._counter += 1

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

Check requirements:
1. `add_job` accepts optional priority (default=0) -> Yes.
2. Higher priority processed first -> Yes, `-priority` ensures 2 comes before 1 before 0.
3. FIFO for same priority -> Yes, `_counter` ensures strict ordering.
4. `get_next_job() -> tuple[str, dict] | None` -> Yes.
5. Priority levels mentioned in comments/example, but logic handles any int.
6. Only Python code, no explanations.

One minor point: The example shows `queue.add_job("normal", {"type": "normal"}, priority=0)`. The signature should match.
Type hints: `add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None`
`get_next_job(self) -> tuple[str, dict] | None`

Let's verify Python version compatibility. `tuple[str, dict] | None` is Python 3.10+. I'll stick to it as requested, or use `Optional[Tuple[str, dict]]` for broader compatibility, but the prompt uses `tuple[str, dict] | None`. I'll use `from typing import Optional, Tuple` or just stick to the prompt's syntax. I'll use standard typing.

Wait, should I include imports? Yes.
The prompt says "Write only the Python code, no explanations."

Let's draft it carefully.

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

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, job_id, job_data))
        self._counter += 1

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

Check if `tuple[str, dict] | None` is preferred. I'll use `Optional[Tuple[str, dict]]` to be safe, or just `tuple[str, dict] | None` as requested. I'll stick to `Optional[Tuple[str, dict]]` or match the prompt exactly. The prompt uses `tuple[str, dict] | None`. I'll use that.

Actually, Python 3.10+ supports `tuple[str, dict] | None`. I'll just use `Optional[Tuple[str, dict]]` for compatibility, or keep the prompt's syntax. It doesn't matter much.

One thing: `job_data` is a dict. In `get_next_job`, should it return a copy or the original? The prompt doesn't specify, so returning the reference is fine.

Let's verify the example:
```python
queue = JobQueue()
queue.add_job("normal", {"type": "normal"}, priority=0)
queue.add_job("critical", {"type": "critical"}, priority=2)
queue.add_job("high", {"type": "high"}, priority=1)

job = queue.get_next_job()  # Returns critical job first
```
Heap after adds:
[(-2, 1, "critical", {...}), (-1, 2, "high", {...}), (0, 0, "normal", {...})]
Pop returns (-2, 1, "critical", ...). Correct.

What about `get_next_job` return type hint? I'll use `Optional[Tuple[str, dict]]` to be safe across versions, or just `tuple[str, dict] | None`. I'll stick to `Optional[Tuple[str, dict]]` as it's standard. Actually, the prompt uses `tuple[str, dict] | None`, I'll use that exactly.

Wait, should I include `from typing import Optional, Tuple`? Yes.

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

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, job_id, job_data))
        self._counter += 1

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

Check requirements again:
1. `add_job` accepts optional priority (default=0) -> Yes.
2. Higher priority first -> Yes.
3. FIFO same priority -> Yes.
4. `get_next_job() -> tuple[str, dict] | None` -> Yes.
5. Only Python code -> Yes.

One detail: `job_id` and `job_data` are positional in the example, but I'll make them named or positional as needed. The example uses `queue.add_job("normal", {"type": "normal"}, priority=0)`, so `job_id` and `job_data` are positional. That's fine.

Should I handle `job_data` copy? Not required.
Should I use `typing` module? Yes.
I'll output exactly the code.

One minor point: `tuple[str, dict] | None` is valid in Python 3.10+. I'll use `Optional[Tuple[str, dict]]` to be safe, or just `tuple[str, dict] | None`. I'll stick to `Optional[Tuple[str, dict]]` as it's more compatible, but the prompt explicitly wrote `tuple[str, dict] | None`. I'll use `Optional[Tuple[str, dict]]` or match the prompt. It doesn't matter much for functionality. I'll use `Optional[Tuple[str, dict]]`.

Actually, I'll just write it without extra imports if possible, but `heapq` and `typing` are standard. I'll include them.

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

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, job_id, job_data))
        self._counter += 1

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

This meets all requirements. I will output only this code.
One thing: `tuple[str, dict] | None` in the prompt might be a hint to use modern typing. I'll use `Optional[Tuple[str, dict]]` to be safe, or just `tuple[str, dict] | None`. I'll stick to `Optional[Tuple[str, dict]]`. Actually, I'll just use `Optional[Tuple[str, dict]]`. It's fine.

Wait, the prompt says `get_next_job() -> tuple[str, dict] | None`. I'll use exactly that for the return type hint to match the prompt.

```python
import heapq
from typing import Tuple

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, job_id, job_data))
        self._counter += 1

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

This is clean and matches the prompt exactly. I'll output this.
All constraints met. No explanations. Ready. 
One minor consideration: `job_data` might be mutated outside, but that's standard. If they want deep copy, they'd specify. I'll leave it.
The code is ready.✅
Output matches request.✅
Proceeds. 
Self-Correction/Verification during thought:
- Does `heapq` handle tuples correctly? Yes, compares element by element. `(-priority, counter)` ensures higher priority first, then lower counter first (FIFO).
- `counter` starts at 0 and increments. Correct.
- `get_next_job` returns `tuple[str, dict] | None`. Correct.
- `add_job` signature matches example. Correct.
- Only Python code. Correct.

All good. Output matches. 
Proceeds. 
[Output Generation] -> *outputs code*
```python
import heapq
from typing import Tuple

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, job_id, job_data))
        self._counter += 1

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._heap:
            return None
        _, _, job_id, job_data = heapq.heappop(self._heap)
        return job_id, job_data
```
Wait, `tuple[str, dict] | None` requires Python 3.10+. The prompt uses it, so it's fine. I'll keep it.
All set.✅
Output matches exactly. 
Proceeds. 
[Final Check]
The prompt asks for "Write only the Python code, no explanations."
I will provide exactly that.
Code looks solid.
Done. 
Output Generation. 
(Self-Correction/Note during generation prep)
I should make sure imports are included. `heapq` is standard. `Tuple` from typing isn't strictly needed if I use `tuple[str, dict]`, but I'll keep it minimal.
Actually, `tuple[str, dict] | None` is valid in 3.10+. I'll just use it.
Ready. 
Output matches response. 
Proceeds. 
[Output] -> *code block*
```python
import heapq

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, job_id, job_data))
        self._counter += 1

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._heap:
            return None
        _, _, job_id, job_data = heapq.heappop(self._heap)
        return job_id, job_data
```
Looks perfect. Matches all constraints. No extra text. Done. 
Proceeds. 
[Final Output Generation] -> *outputs the code block*
(Note: I'll remove `from typing import Tuple` since I'm using built-in `tuple` syntax as in the prompt, to keep it clean.)
All good. 
Done. 
[Output Generation] (matches the final code block)
```python
import heapq

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, job_id, job_data))
        self._counter += 1

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._heap:
            return None
        _, _, job_id, job_data = heapq.heappop(self._heap)
        return job_id, job_data
```
Done. 
(Self-Correction/Verification during output prep)
Wait, should I handle `job_data` copy to prevent external mutation affecting queue? Not required by prompt.
Should I use `typing.Optional`? The prompt uses `| None`, so I'll stick with that.
All good.
Output matches.✅
Proceeds. 
[Final Output Generation] -> *Proceeds*

</reasoning_content>

<content>
```python
import heapq

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, job_id, job_data))
        self._counter += 1

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