<reasoning_content>
Here's a thinking process:

1.  **Understand the Goal**: The user wants a Python class `JobQueue` that implements priority-based job scheduling. It needs to support adding jobs with an optional priority (default 0), retrieving the highest priority job next, maintaining FIFO order for same-priority jobs, and returning a tuple of (job_name, job_data) or None if empty.

2.  **Key Requirements**:
   - `add_job(job_name: str, job_data: dict, priority: int = 0) -> None`
   - Higher priority = processed first (so priority 2 > 1 > 0)
   - FIFO for same priority
   - `get_next_job() -> tuple[str, dict] | None`
   - Priority levels: 0 (Normal), 1 (High), 2 (Critical)
   - Only output Python code, no explanations.

3.  **Data Structure Choice**:
   - Need a priority queue that maintains insertion order for ties.
   - Python's `heapq` module is perfect for this. It's a min-heap by default.
   - To make higher priority values come out first, I can store negative priorities in the heap, or use a custom comparator (not directly supported in heapq), or just negate the priority.
   - To maintain FIFO order for same priorities, I'll use a monotonic counter as a tie-breaker in the heap tuple: `(-priority, counter, job_name, job_data)`
   - `heapq.heappush` and `heapq.heappop` will handle the ordering.

4.  **Class Design**:
   ```python
   import heapq
   import itertools

   class JobQueue:
       def __init__(self):
           self._queue = []
           self._counter = itertools.count() # or just a simple integer counter

       def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
           # Store as (-priority, counter, job_name, job_data)
           heapq.heappush(self._queue, (-priority, next(self._counter), job_name, job_data))

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

5.  **Check Requirements**:
   - `add_job` accepts optional priority parameter (default=0) -> Yes
   - Higher priority jobs processed before lower -> Yes, using `-priority` in min-heap
   - Same priority maintains FIFO -> Yes, using `itertools.count()` or simple counter as tie-breaker
   - `get_next_job() -> tuple[str, dict] | None` -> Yes
   - Priority levels: 0, 1, 2 -> Handled naturally
   - Example matches behavior -> Yes
   - Only Python code -> Yes

   One minor thing: `itertools.count()` is fine, but a simple integer counter is also fine. I'll stick with `itertools.count()` for elegance, or just `self._counter = 0` and increment. Both work. I'll use a simple integer counter for clarity, or `itertools.count()`. Actually, `itertools.count()` is standard for this.

   Wait, the type hint `tuple[str, dict] | None` requires Python 3.10+. The prompt uses it, so I'll keep it. I should also add type hints to `add_job` for completeness, though not strictly required. I'll stick to the signature requested.

   Let's verify the heap tuple structure: `(-priority, counter, job_name, job_data)`
   When priorities are equal, heap compares counters. Since counter increases, lower counter (earlier insertion) comes first. Correct.
   `heapq` is a min-heap, so `-priority` means higher priority (e.g., 2 becomes -2) will be popped first. Correct.

   One detail: `job_data` is a dict. If it's mutable, that's fine. The prompt says `dict`.
   I'll make sure the code is clean and matches exactly.

   Code structure:
   ```python
   import heapq
   import itertools

   class JobQueue:
       def __init__(self):
           self._queue = []
           self._counter = itertools.count()

       def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
           heapq.heappush(self._queue, (-priority, next(self._counter), job_name, job_data))

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

   Wait, should I include `from typing import ...`? The prompt uses `tuple[str, dict] | None` which is built-in in 3.10+. I'll just use it as is.
   I'll add `import heapq` and `import itertools` at the top.
   The prompt says "Write only the Python code, no explanations."
   I'll output exactly that.

   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:
   - normal: (-0, 0, "normal", ...)
   - critical: (-2, 1, "critical", ...)
   - high: (-1, 2, "high", ...)
   Heap order: (-2, 1), (-1, 2), (-0, 0)
   Pop gives critical. Correct.
   Next pop gives high. Correct.
   Next gives normal. Correct.
   FIFO for same priority: if two jobs have priority 0, counters 0 and 3, heap compares (-0, 0) vs (-0, 3), 0 comes first. Correct.

   All requirements met. I will produce the code.
   One minor point: `tuple[str, dict] | None` syntax is Python 3.10+. I'll keep it as requested.
   I'll make sure the class name is `JobQueue` as implied by the example.
   I'll include type hints for `add_job` as well for consistency, though not strictly required.
   The code is ready.
   Output matches exactly.
   Proceed. 
   Self-Correction/Refinement during thought: 
   Should I use `typing.Optional` or `| None`? The prompt uses `| None`, so I'll stick with that. It's fine.
   I'll just output the class and imports.
   Done. 
   Output generation. 
   ```python
   import heapq
   import itertools

   class JobQueue:
       def __init__(self):
           self._queue = []
           self._counter = itertools.count()

       def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
           heapq.heappush(self._queue, (-priority, next(self._counter), job_name, job_data))

       def get_next_job(self) -> tuple[str, dict] | None:
           if not self._queue:
               return None
           _, _, job_name, job_data = heapq.heappop(self._queue)
           return job_name, job_data
   ```
   This matches all constraints. No extra text. Ready. 
   One thing: The prompt says "Write only the Python code, no explanations." I will strictly follow that.
   All good.✅
   Output matches response. 
   Proceeds. 
   [Output Generation] -> *Proceeds*
   *(Self-Correction/Verification during generation prep)*: 
   Wait, `itertools.count()` returns an iterator. `next(self._counter)` works. Alternatively, I can just use a simple integer counter to avoid importing itertools if preferred, but itertools is standard. I'll stick with itertools or just `self._counter = 0` and `self._counter += 1`. Both are fine. I'll use a simple integer counter for brevity, or keep itertools. Actually, `itertools.count()` is more Pythonic for this. I'll keep it.
   The type hint `tuple[str, dict] | None` is fine.
   I'll output exactly the code.
   Done. 
   [Output] -> *Proceeds*

</reasoning_content>

<content>
```python
import heapq
import itertools

class JobQueue:
    def __init__(self):
        self._queue = []
        self._counter = itertools.count()

    def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
        heapq.heappush(self._queue, (-priority, next(self._counter), job_name, job_data))

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        _, _, job_name, job_data = heapq.heappop(self._queue)
        return job_name, job_data
```
</content>