<reasoning_content>
The user wants a Python implementation of a priority-based job scheduling queue.
Key requirements:
1. `add_job(job_id, job_data, priority=0)`: Adds a job with an optional priority.
2. Higher priority jobs are processed first.
3. Same priority jobs maintain FIFO order.
4. `get_next_job() -> tuple[str, dict] | None`: Retrieves and removes the highest priority job.
5. Priority levels: 0 (Normal, default), 1 (High), 2 (Critical).
6. Output only Python code.

Implementation details:
- Use a data structure that supports priority queue operations efficiently. `heapq` in Python is suitable.
- Since higher priority values should come out first, but `heapq` is a min-heap, I need to negate the priority value when pushing to the heap.
- To maintain FIFO order for same priorities, I need a counter to track insertion order. The heap will store tuples: `(-priority, insertion_counter, job_id, job_data)`.
- `add_job` will increment the counter and push to the heap.
- `get_next_job` will pop from the heap and return `(job_id, job_data)`. If empty, return `None`.

Let's draft the class:

```python
import heapq

class JobQueue:
    def __init__(self):
        self._queue = []
        self._counter = 0  # To maintain FIFO order for same priorities

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        # Negate priority because heapq is a min-heap, but we want higher priority first
        # Counter ensures FIFO for same priority
        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 the highest priority (lowest negative priority value)
        # and lowest counter (earliest insertion)
        _, _, job_id, job_data = heapq.heappop(self._queue)
        return (job_id, job_data)
```

Wait, checking the requirements again:
"Higher priority jobs should be processed before lower priority jobs"
Priority levels: 0: Normal, 1: High, 2: Critical.
So 2 is highest, 0 is lowest.
Negating priority: -2 < -1 < -0. So -2 comes first in min-heap. Correct.

Check type hints: `tuple[str, dict] | None` is valid in Python 3.10+. I'll use it as requested.
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.

One minor point: `add_job` signature in requirements says `add_job` to accept an optional priority parameter (default=0). It doesn't explicitly show `job_id` and `job_data` in the signature, but the example shows them. I'll assume `add_job(self, job_id, job_data, priority=0)`.

Let's refine the code to be clean and strictly follow requirements.

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

Wait, should I include type hints as in the prompt? The prompt says `get_next_job() -> tuple[str, dict] | None`. I'll stick to that.
Also, the prompt says "Write only the Python code, no explanations."

One thing to consider: `heapq` compares tuples element by element. `(-priority, counter, job_id, job_data)`. If priorities are equal, it compares `counter`. Since `counter` increases, smaller `counter` comes first, preserving FIFO. This is correct.

Is there any edge case? `job_id` and `job_data` might be compared if `counter` is somehow equal, but `counter` is unique per job, so it won't happen. To be strictly safe, I could make `counter` the second element, which I did. Actually, `(-priority, self._counter, job_id, job_data)` is fine.

Let's verify the example:
add_job("normal", ..., 0) -> push (0, 0, "normal", ...)
add_job("critical", ..., 2) -> push (-2, 1, "critical", ...)
add_job("high", ..., 1) -> push (-1, 2, "high", ...)
Heap: [(-2, 1, "critical", ...), (0, 0, "normal", ...), (-1, 2, "high", ...)]
heappop returns (-2, 1, "critical", ...). Correct.

One minor adjustment: Python's `heapq` might complain if `job_data` is compared when `counter` is somehow equal, but `counter` is strictly increasing, so it's fine. To be perfectly safe and avoid any potential comparison issues with dict objects in older Python versions (though Python 3 doesn't allow comparing dicts with `<`, but `heapq` only compares up to the first differing element), I'll keep `counter` as the second element. Actually, `heapq` compares tuples left-to-right. Since `counter` is unique, it will never reach `job_id` or `job_data`. So it's safe.

I will write the complete 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:
        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
```

Check requirements:
1. Modify `add_job` to accept an optional priority parameter (default=0) -> Done.
2. Higher priority jobs should be processed before lower priority jobs -> Done (negated priority).
3. Jobs with the same priority should maintain FIFO order -> Done (counter).
4. Add `get_next_job() -> tuple[str, dict] | None` -> Done.
Priority levels mentioned are just for context, implementation handles integers.

The prompt says "Write only the Python code, no explanations."
I will output exactly the code.

One detail: `tuple[str, dict] | None` syntax requires Python 3.10+. I'll assume it's acceptable as it's in the prompt. Alternatively, I can use `typing.Optional` and `typing.Tuple` for broader compatibility, but the prompt uses the modern syntax, so I'll stick with it.

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

Looks solid. I will output this.
Wait, should I include `from typing import ...`? The prompt uses `tuple[str, dict] | None`, which is built-in in 3.9+ for `tuple` but `|` union syntax is 3.10+. I'll just use the prompt's syntax. It's fine.

One thing: `add_job` signature in the prompt example doesn't show type hints, but I'll add them for clarity, or keep it simple. The prompt says "Modify `add_job` to accept an optional priority parameter (default=0)". I'll stick to a clean implementation.

I'll produce the code now. 
All constraints met. No extra text.
Proceed. 
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 good. I will output it exactly. 
One minor point: sometimes prompts expect just the class definition without imports if not specified, but `heapq` is standard and necessary for priority queue. I'll include `import heapq`.
All good. 
Output matches. 
Proceeds. 
[Output Generation] -> *Self-Correction/Verification during thought*: 
Wait, should `add_job` signature match exactly `add_job(self, job_id, job_data, priority=0)`? Yes.
The prompt example: `queue.add_job("normal", {"type": "normal"}, priority=0)`
My code matches.
I will output the code block.✅
Done. 
Output Generation. 
```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 perfect. Matches all constraints. No extra text. Proceeds. 
(Self-Correction/Note during generation prep: I'll make sure the type hint matches exactly `tuple[str, dict] | None` as requested. Python 3.10+ supports this. It's fine.)
All set. 
Output matches response. 
[Done]

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