<reasoning_content>
Here's a thinking process:

1.  **Understand User Requirements:**
   - Implement priority-based job scheduling for a queue.
   - Modify `add_job` to accept an optional `priority` parameter (default=0).
   - Higher priority jobs processed first.
   - Same priority jobs maintain FIFO order.
   - Add `get_next_job() -> tuple[str, dict] | None` to get the highest priority job.
   - Priority levels: 0 (Normal), 1 (High), 2 (Critical).
   - Output only Python code, no explanations.

2.  **Identify Key Data Structures:**
   - Need a queue that supports priority ordering while maintaining FIFO for same priorities.
   - Python's `heapq` module is perfect for priority queues. It maintains a min-heap, but we want higher priority numbers to be processed first. So we can negate the priority or use a custom comparison.
   - To maintain FIFO for same priorities, we need a tie-breaker: a counter/timestamp that increments with each job added. Since `heapq` is a min-heap, we can store tuples like `(-priority, insertion_order, job_id, job_data)`.
   - Alternatively, use a dictionary of deques for each priority level, but `heapq` is simpler and standard.

3.  **Design the Class:**
   ```python
   import heapq
   from typing import Optional

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

       def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
           # Use negative priority so higher priority numbers come first in min-heap
           # Use counter to maintain FIFO order for same priority
           heapq.heappush(self._heap, (-priority, self._counter, job_id, job_data))
           self._counter += 1

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

4.  **Check Requirements:**
   - `add_job` accepts optional priority (default=0)? Yes.
   - Higher priority processed first? Yes, using `-priority`.
   - Same priority maintains FIFO? Yes, using `_counter`.
   - `get_next_job() -> tuple[str, dict] | None`? Yes.
   - Priority levels 0, 1, 2? Handled naturally.
   - Only Python code? Yes.

   Wait, the type hint in the requirement is `tuple[str, dict] | None`. In Python 3.10+, this is valid. I'll use `from typing import Optional` or just write the hint as requested. The prompt says `tuple[str, dict] | None`, so I'll stick to that or use `Optional[tuple[str, dict]]`. I'll use the exact hint style if possible, but standard typing is fine.

   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 out first. Correct.
   - If priorities are equal, `-priority` is equal, so it compares `counter`. Lower counter (earlier insertion) comes out first. Correct.
   - `job_id` and `job_data` are not compared because `counter` is unique and breaks ties. But to be safe, we can ensure `job_id` and `job_data` are not compared, or just rely on `counter` being unique. Actually, `heapq` might compare the 3rd and 4th elements if the first two are equal, but `counter` is strictly increasing, so they'll never be equal. So it's safe.

   One minor thing: `job_data` might not be comparable, but since `counter` is unique, it won't reach that comparison. Still, to be strictly safe, we could store just `(-priority, counter, (job_id, job_data))` or rely on the counter. I'll stick with the tuple as is, it's standard.

   Let's write it out carefully.

   ```python
   import heapq
   from typing import Optional

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

   Check type hints: `tuple[str, dict] | None` requires Python 3.10+. I'll use it as requested. If needed, I can add `from __future__ import annotations` for compatibility, but it's probably fine.

   Let's double-check the exact requirement: "Modify `add_job` to accept an optional priority parameter (default=0)". The signature in the example is `queue.add_job("normal", {"type": "normal"}, priority=0)`. So `job_id`, `job_data`, `priority`.

   All good.

   One thing: `job_data` is a dict. Dictionaries are not comparable in Python 3, but as mentioned, `counter` ensures no tie-breaking reaches `job_data`. To be absolutely safe against any edge cases or older Python versions where heap might try to compare, I could wrap the payload: `heapq.heappush(self._heap, (-priority, self._counter, (job_id, job_data)))`. But it's fine either way. I'll stick to the simpler tuple unpacking.

   Actually, to be perfectly safe and clean:
   ```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 self._heap:
               _, _, job_id, job_data = heapq.heappop(self._heap)
               return job_id, job_data
           return None
   ```

   This matches all requirements. I'll output just this code.
   One minor adjustment: The prompt says `get_next_job() -> tuple[str, dict] | None`. I'll use that exact type hint.
   I'll add `import heapq` at the top.
   All set.
   Output matches requirement. No extra text.
   Proceed. 
   Self-Correction/Verification during thought:
   - Does `heapq` guarantee FIFO for same priority? Yes, because of the counter.
   - Does it handle higher priority first? Yes, negative priority in min-heap.
   - Type hints: `tuple[str, dict] | None` is valid in 3.10+. I'll use it.
   - The example shows `queue.add_job("normal", {"type": "normal"}, priority=0)`. So parameters are `job_id`, `job_data`, `priority`.
   - Everything is correct.

   Final 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 self._heap:
               _, _, job_id, job_data = heapq.heappop(self._heap)
               return job_id, job_data
           return None
   ```
   This is concise and correct. I will output exactly this.✅
   Note: I'll make sure to include `from typing import Optional` if needed, but the prompt uses `| None` syntax, so I'll stick to that. Python 3.10+ supports it natively. If they want broader compatibility, `from __future__ import annotations` could be added, but it's not strictly necessary. I'll just output the code as is.
   All good. 
   Output matches response. 
   Proceeds. 
   [Output Generation] -> *Self-Correction/Refinement during generation prep*: 
   I should ensure the type hint matches exactly what's requested. The prompt says `get_next_job() -> tuple[str, dict] | None`. I'll use that.
   Code is ready.✅
   Output matches. 
   [Done] 
   *Self-Correction/Note during thought*: Actually, `job_data` might contain unhashable/uncomparable types, but since `counter` is unique, `heapq` will never compare `job_id` or `job_data`. It's safe.
   I'll output the code now. 
   ```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 self._heap:
               _, _, job_id, job_data = heapq.heappop(self._heap)
               return job_id, job_data
           return None
   ```
   Matches perfectly. No extra text. Proceeds. 
   [Output Generation] -> *Output matches the refined code.* 
   *(Done.)*

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