<reasoning_content>
The user wants a priority-based job scheduling queue implementation in Python.
Requirements:
1. `add_job` should accept an optional `priority` parameter (default=0)
2. Higher priority jobs processed first
3. Same priority maintains FIFO order
4. `get_next_job()` returns `(job_id, job_data)` or `None`
5. Priority levels: 0 (Normal), 1 (High), 2 (Critical)

I need to implement a `JobQueue` class.
To maintain FIFO order within the same priority, I can use a priority queue (heapq) with a tuple like `(-priority, sequence_number, job_id, job_data)`. The negative priority ensures higher priority values come first in a min-heap. The sequence number ensures FIFO for same priority.

Let's design it:
```python
import heapq
from typing import 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:
        # Use negative priority so higher priority comes first in min-heap
        # Use counter to maintain FIFO for same priority
        heapq.heappush(self._queue, (-priority, self._counter, job_id, job_data))
        self._counter += 1

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

Check requirements:
1. `add_job` accepts optional priority (default=0) -> Yes
2. Higher priority first -> Yes, using `-priority` in heap
3. Same priority FIFO -> Yes, using `_counter`
4. `get_next_job() -> tuple[str, dict] | None` -> Yes
5. Example matches -> Yes

Wait, the type hint in the requirement says `tuple[str, dict] | None`. I'll use that or `Optional[tuple[str, dict]]`. Python 3.10+ supports `|`. I'll stick to standard typing or just use the exact signature.

Let's verify the heap behavior:
`heapq` is a min-heap.
Tuples are compared element by element.
`(-priority, counter, job_id, job_data)`
If priority is 2, `-priority` is -2.
If priority is 1, `-priority` is -1.
-2 < -1, so priority 2 comes before priority 1. Correct.
If priorities are equal, counters determine order. Lower counter comes first. Correct.

One minor point: `job_data` might not be comparable, but since `counter` is unique, we never compare `job_data`. So it's safe.

I'll write the complete class.

Should I include imports? Yes, `import heapq` and `from typing import Optional` or just use `tuple[str, dict] | None` as requested.

Let's write it out carefully.

```python
import heapq
from typing import 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 self._queue:
            _, _, job_id, job_data = heapq.heappop(self._queue)
            return job_id, job_data
        return None
```

Check type hints: Python 3.10+ supports `tuple[str, dict] | None`. I'll use that directly as in the prompt. If compatibility is a concern, I could use `Optional[tuple[str, dict]]`, but the prompt explicitly shows `tuple[str, dict] | None`. I'll stick to that.

Wait, should `add_job` have type hints? The prompt doesn't specify, but it's good practice. I'll add them.

One thing: `job_data` in `get_next_job` returns a dict. The heap stores it directly. That's fine.

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
```
This matches.

Is there any edge case? Empty queue returns None. Correct.
Same priority FIFO: counter ensures that. Correct.
Higher priority first: negative priority in min-heap ensures that. Correct.

I think this is solid. I'll output just the code as requested.
One minor adjustment: I'll make sure the type hints match exactly what's expected. The prompt says `get_next_job() -> tuple[str, dict] | None`. I'll use that.

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 self._queue:
            _, _, job_id, job_data = heapq.heappop(self._queue)
            return job_id, job_data
        return None
```

Wait, should I include `from typing import Optional`? Not strictly necessary if using `|`. I'll keep it simple.
The prompt says "Write only the Python code, no explanations."
I will output exactly that.
All requirements met.
Done. 
Let's verify one more time: 
- `add_job` accepts optional priority (default=0) -> checked
- Higher priority first -> checked
- Same priority FIFO -> checked via counter
- `get_next_job` returns correct type -> checked
- Example works -> checked

Ready.✅
Output matches response. 
Proceeds. 
[self-correction/verification during thought]
Could `job_data` contain unhashable/uncomparable items? Yes, but since `counter` is unique, heap comparison stops at `counter`. So it's safe.
Type hints: `tuple[str, dict] | None` is valid in Python 3.10+. I'll assume modern Python.
Everything looks good. 
Output matches the final code block. 
Proceeds. 
[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 self._queue:
            _, _, job_id, job_data = heapq.heappop(self._queue)
            return job_id, job_data
        return None
```
</content>