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

To implement this, I can use Python's `heapq` module for efficient priority queue operations, or a sorted list. Since we need FIFO for same priorities, I'll use a counter or timestamp as a secondary sort key.

Let's use `heapq`. Each element in the heap will be a tuple: `(priority, counter, job_id, job_data)`.
- `priority`: determines order (higher priority first, so we might need to negate it if using min-heap, or just use a max-heap simulation. Actually, `heapq` is a min-heap. To get higher priority first, we can store `-priority`.
- `counter`: ensures FIFO for same priorities. Since `heapq` pops the smallest, we want the smallest counter for the earliest added job. So just use an incrementing counter.
- `job_id`: string identifier.
- `job_data`: dictionary.

Wait, the requirement says "Higher priority jobs should be processed before lower priority jobs". So priority 2 > priority 1 > priority 0.
If we use `heapq` (min-heap), we can store `(-priority, counter, job_id, job_data)`. Then the smallest `-priority` corresponds to the largest `priority`.
Alternatively, we can just use a list and sort it, but `heapq` is O(log n) for insertion and O(log n) for extraction, which is better. Given it's a simple implementation, either works. I'll stick with `heapq` for efficiency.

Let's draft the 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:
        # Negate priority so that higher priority comes first in min-heap
        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
        # Pop the item with highest priority (smallest -priority) and lowest counter
        _, _, job_id, job_data = heapq.heappop(self._queue)
        return job_id, job_data
```

Wait, the requirement says `get_next_job() -> tuple[str, dict] | None`. The type hint uses `|` which is Python 3.10+. I'll assume modern Python.
Also, the example shows:
```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
```

My implementation matches this.
Let's double check the requirements:
1. `add_job` accepts optional priority (default=0) -> Done.
2. Higher priority processed first -> Done (using -priority).
3. Same priority FIFO -> Done (using counter).
4. `get_next_job() -> tuple[str, dict] | None` -> Done.
5. Priority levels: 0, 1, 2 -> Handled naturally by the integer priority.

One minor thing: `heapq` compares tuples element by element. `(-priority, counter, job_id, job_data)`. If priorities are equal, it compares `counter`, which is strictly increasing, so FIFO is preserved. `job_id` and `job_data` won't be compared unless counters are equal, which they won't be.

Is there any need to handle `job_data` comparison? No, because counters are unique.
The code looks solid.

Let's format it properly. I'll include necessary imports. I'll make sure the class name is `JobQueue` as implied by the example.

One thing: `tuple[str, dict] | None` syntax requires Python 3.10+. I'll keep it as requested. Alternatively, I can use `typing.Optional` and `typing.Tuple` for broader compatibility, but the prompt uses `|`, so I'll stick to it or just use standard hints. I'll keep it as is.

Wait, should `add_job` return anything? The example doesn't show a return value, so `None` is fine.

Let's verify the exact prompt: "Write only the Python code, no explanations."

I will produce just the code.

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

Check requirements again:
- `add_job` accepts optional priority parameter (default=0) -> Yes.
- Higher priority jobs processed before lower -> Yes, `-priority` ensures 2 becomes -2, which is smaller than -1 and 0, so popped first.
- Same priority FIFO -> Yes, `_counter` ensures order.
- `get_next_job() -> tuple[str, dict] | None` -> Yes.
- Example matches -> Yes.

One detail: `tuple[str, dict] | None` is valid in Python 3.10+. I'll keep it. If they want older compatibility, they'd use `Optional[Tuple[str, dict]]`, but I'll stick to the prompt's syntax.

Is there any edge case? Empty queue returns None. Correct.
Priority levels 0, 1, 2 are just integers, handled correctly.
The code is concise and meets all requirements.

I will output only the Python code as requested.
One minor point: sometimes `heapq` might compare `job_id` or `job_data` if `_counter` collisions occur, but `_counter` is strictly increasing, so no collisions. It's safe.

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

Looks good. I'll output this.
Wait, should I include `from typing import ...`? The prompt uses `|` which is built-in in 3.10+. I'll just use the prompt's syntax.
All good.
Proceeding. 
One thing: `tuple[str, dict] | None` might cause a syntax error in Python < 3.10, but the prompt uses it, so it's fine.
I'll output exactly the code.
Done. 
Let's double check the prompt: "Write only the Python code, no explanations."
I will provide just the code block.
All requirements met.
Ready. 
Output matches the refined code.✅
Note: I'll make sure indentation is correct.
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 is correct. I will output it. 
Self-Correction/Verification during thought:
Could there be a requirement to handle priority levels explicitly? The prompt says "Priority levels: 0: Normal (default), 1: High, 2: Critical". This is just documentation, the code handles integers generically, which is fine.
The example uses `queue = JobQueue()`, so class name must be `JobQueue`.
Everything matches.
Output matches request.✅
Proceeds. 
[Output Generation] -> *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.✅
Done. 
Output matches exactly. 
(Self-Correction/Refinement during thought)
Wait, should I use `typing` module for compatibility? The prompt uses `|` which is Python 3.10+. I'll stick to it. It's fine.
One detail: `tuple[str, dict] | None` is valid in 3.10+. I'll keep it.
The code is ready.
Output matches. 
Proceeds. 
[Final Check]
- `add_job` accepts optional priority (default=0) -> Yes
- Higher priority first -> Yes (`-priority`)
- FIFO for same priority -> Yes (`self._counter`)
- `get_next_job()` signature -> Yes
- Example works -> Yes
- Only Python code -> Yes
All constraints satisfied.
Output generation. 
[Done] 
```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
```
Matches request. Outputting. 
(Note: I'll remove the markdown code block if the prompt says "Write only the Python code", but usually markdown is fine. I'll just output the raw code or markdown as standard. I'll stick to markdown for clarity, but the prompt says "Write only the Python code, no explanations." I will provide just the code block.)
Actually, to be strictly compliant, I'll output just the code without extra text.
Done. 
[Output Generation] -> *Proceeds*

</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>