<reasoning_content>
The user wants a Python implementation of a priority-based job scheduling queue.
Key requirements:
1. Class `JobQueue` with methods `add_job` and `get_next_job`.
2. `add_job` accepts job_id, data, and optional `priority` (default 0).
3. Higher priority value -> processed first.
4. Same priority -> FIFO order.
5. `get_next_job` returns a tuple `(job_id, data)` or `None`.
6. Priority levels: 0 Normal, 1 High, 2 Critical.

Implementation details:
- Need a way to store jobs sorted by priority and insertion order.
- Python's `heapq` module is suitable for priority queues.
- However, `heapq` only handles the minimum element by default. Since higher priority means higher value, we can store negative priority in the heap, or use a custom comparator (not directly supported in `heapq` for tuples without care).
- Alternatively, since priorities are small integers (0, 1, 2), we could use separate queues for each priority level. This is often simpler and guarantees FIFO within each priority.
- Let's consider the heap approach first.
  - Heap elements: `(-priority, insertion_order, job_id, data)`
  - `insertion_order` ensures FIFO for same priority.
  - `get_next_job` pops the smallest item (which corresponds to highest priority due to negation).
- Let's consider the separate queues approach.
  - Dictionary of queues: `{priority: deque()}`.
  - `add_job`: append to `queues[priority]`.
  - `get_next_job`: find the highest priority key with a non-empty queue, pop from it.
  - This might be slower if there are many priority levels, but for small fixed levels it's fine. The prompt implies a general priority system but gives specific levels.
  - The heap approach is more general and efficient for arbitrary priorities.

Let's stick to the heap approach as it's standard for priority queues.
Wait, the prompt says "Higher priority jobs should be processed before lower priority jobs". So priority 2 > priority 1 > priority 0.
Using `heapq`, we push `(-priority, counter, job_id, data)`. `counter` maintains insertion order.
`get_next_job` pops and returns `(job_id, data)`.

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, data: dict, priority: int = 0) -> None:
        # Use negative priority so that higher priority comes first in min-heap
        heapq.heappush(self._queue, (-priority, self._counter, job_id, data))
        self._counter += 1
        
    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        _, _, job_id, data = heapq.heappop(self._queue)
        return (job_id, data)
```

Check requirements:
1. `add_job` accepts optional priority (default=0) -> Yes.
2. Higher priority processed first -> Yes, `-priority` ensures 2 becomes -2, which is smaller than -1 and 0, so it pops first.
3. Same priority maintains FIFO -> Yes, `_counter` increases monotonically, so smaller counter comes first.
4. `get_next_job() -> tuple[str, dict] | None` -> Yes.
5. Priority levels example matches.

One minor thing: type hints in Python 3.9+ support `tuple[str, dict] | None`. The prompt uses it, so I'll include it. I'll add `from __future__ import annotations` or just use standard typing if needed, but modern Python handles it. I'll stick to the prompt's style.

Wait, should I handle the case where `data` is not a dict? The prompt says `data: dict`. I'll keep it as is.
Also, `heapq` is standard library. I'll import it.

Let's double check 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
```
With my implementation:
push: (0, 0, "normal", {...})
push: (-2, 1, "critical", {...})
push: (-1, 2, "high", {...})
Heap order: (-2, 1, ...), (-1, 2, ...), (0, 0, ...)
pop returns (-2, 1, "critical", ...). Correct.

What about `get_next_job` return type? The prompt says `tuple[str, dict] | None`. I'll add that to the signature.

One thing: `heapq` compares tuples element by element. If priorities are equal, it compares `_counter`, which is unique and increasing, so FIFO is preserved. If `_counter` were not unique, it would compare `job_id` (string), which might break FIFO or cause issues if `job_id` isn't comparable. But `_counter` is unique, so it's fine.

Let's write out the full code.
I'll include necessary imports and type hints.
I'll make sure it matches the prompt exactly.

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

class JobQueue:
    def __init__(self) -> None:
        self._queue: list = []
        self._counter: int = 0
        
    def add_job(self, job_id: str, data: Dict, priority: int = 0) -> None:
        heapq.heappush(self._queue, (-priority, self._counter, job_id, data))
        self._counter += 1
        
    def get_next_job(self) -> Optional[Tuple[str, Dict]]:
        if not self._queue:
            return None
        _, _, job_id, data = heapq.heappop(self._queue)
        return (job_id, data)
```

Wait, the prompt uses `tuple[str, dict] | None` syntax which is Python 3.10+. I'll stick to that or use `typing.Optional`. I'll use the prompt's syntax for consistency, or just standard `typing`. It's fine either way. I'll use `from __future__ import annotations` to be safe, or just stick to `typing`. Actually, I'll just write it cleanly.

Let's verify requirement 3: "Jobs with the same priority should maintain FIFO order". The `_counter` ensures this.
Requirement 4: `get_next_job() -> tuple[str, dict] | None`. I'll use exactly that signature.

One detail: `heapq` stores tuples. If `job_id` or `data` contain uncomparable objects, it might raise an error when heapify or push, but since `_counter` is unique, it won't compare further elements. So it's safe.

I will produce the code now.
I'll make sure imports are included.
The prompt says "Write only the Python code, no explanations."

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

class JobQueue:
    def __init__(self) -> None:
        self._queue = []
        self._counter = 0
        
    def add_job(self, job_id: str, data: dict, priority: int = 0) -> None:
        heapq.heappush(self._queue, (-priority, self._counter, job_id, data))
        self._counter += 1
        
    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        _, _, job_id, data = heapq.heappop(self._queue)
        return (job_id, data)
```
This looks solid. Matches all requirements. Uses standard library. Handles priorities correctly. Maintains FIFO. Type hints match prompt.
I'll output this.
One minor point: `tuple[str, dict] | None` requires Python 3.10+. I'll assume modern Python. If I want to be strictly compatible, I could use `Optional[Tuple[str, dict]]`, but the prompt uses the `|` syntax, so I'll stick with it. I'll add `from __future__ import annotations` just in case, or leave it as is. It's fine.

I will produce the code exactly as requested.
All good. 
Proceed. 
Output matches the code block. 
No extra text. 
Done. 
Let's generate. 
(Self-Correction/Refinement during thought)
Wait, should I handle the case where `data` might not be a dict? The prompt says `data: dict`. I'll keep it.
Is there any chance `heapq` behavior with tuples changes if `job_id` is compared? No, because `_counter` is unique, so Python stops comparing after `_counter`.
The code is robust.
I'll output it. 
One thing: `from typing import Optional, Tuple, Dict` is not strictly needed if I use `tuple[str, dict] | None`, but I'll keep imports minimal. I'll just use `import heapq`.
Actually, I'll stick to the prompt's type hint style.
Final code:
```python
import heapq

class JobQueue:
    def __init__(self) -> None:
        self._queue = []
        self._counter = 0
        
    def add_job(self, job_id: str, data: dict, priority: int = 0) -> None:
        heapq.heappush(self._queue, (-priority, self._counter, job_id, data))
        self._counter += 1
        
    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        _, _, job_id, data = heapq.heappop(self._queue)
        return (job_id, data)
```
This is clean and meets all requirements. I will output this. 
All steps verified. 
Proceeds. 
[Output Generation] -> *outputs code*
(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 parameter (default=0)
Check requirement 2: Higher priority processed first
Check requirement 3: Same priority FIFO
All satisfied.
Code is ready.✅
Output matches response. 
Proceeds. 
[Output Generation]
```python
import heapq

class JobQueue:
    def __init__(self) -> None:
        self._queue = []
        self._counter = 0
        
    def add_job(self, job_id: str, data: dict, priority: int = 0) -> None:
        heapq.heappush(self._queue, (-priority, self._counter, job_id, data))
        self._counter += 1
        
    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        _, _, job_id, data = heapq.heappop(self._queue)
        return (job_id, data)
```
Looks good. I will provide exactly this. 
No extra text. 
Done. 
[Final Check]
The prompt asks for "Write only the Python code, no explanations."
I will output just the code block.
All good. 
Proceeds. 
[Output Generation] -> *outputs the code*
(Note: I'll make sure indentation is correct)
Done. 
[Final Output Generation] (matches the response)
```python
import heapq

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

</reasoning_content>

<content>
```python
import heapq

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