<reasoning_content>
Thinking Process:

1.  **Analyze the Request:**
    *   Goal: Implement a priority-based job scheduling queue in Python.
    *   Requirement 1: Modify `add_job` to accept an optional `priority` parameter (default=0).
    *   Requirement 2: Higher priority jobs processed before lower priority jobs (1 > 0, 2 > 1, etc.).
    *   Requirement 3: FIFO order for same priority.
    *   Requirement 4: Add `get_next_job() -> tuple[str, dict] | None`.
    *   Priority Levels: 0 (Normal), 1 (High), 2 (Critical).
    *   Output: Only Python code, no explanations.

2.  **Determine Data Structure:**
    *   To maintain priority order while respecting FIFO for same priorities, a heap is a common choice.
    *   Python's `heapq` module implements a min-heap.
    *   To ensure higher priority (e.g., 2) is processed before lower priority (e.g., 0), we can store priority as a negative value (so -2 < -0, meaning 2 comes before 0) OR use `heapq` with a custom comparator (though standard `heapq` compares tuples element-wise).
    *   To maintain FIFO for same priorities, we need a tie-breaker. Since `heapq` compares tuples, we can add a sequence counter to the tuple stored in the heap.
    *   Tuple structure in heap: `(priority, sequence, job_id, job_data)`
    *   Priority logic:
        *   If 2 is Critical and 0 is Normal, and we want 2 first.
        *   Standard min-heap pops smallest.
        *   If we store priority as is (0, 1, 2), 0 pops first. We want 2 first.
        *   Option A: Store `-priority`. Then -2 < -0. 2 comes before 0.
        *   Option B: Store `(priority, ...)` but invert logic? No, standard heap is min-heap.
        *   Let's check the requirement: "Higher priority jobs should be processed before lower priority jobs".
        *   Example: `priority=2` (Critical) vs `priority=0` (Normal). 2 > 0. So 2 should come out first.
        *   In a min-heap, smaller values come out first.
        *   So we should store `-priority`.
        *   Wait, the example says:
            ```python
            queue.add_job("critical", ..., priority=2)
            queue.add_job("normal", ..., priority=0)
            job = queue.get_next_job() # Returns critical job first
            ```
        *   So Priority 2 > Priority 0.
        *   If I use `-priority`: -2 < 0. -2 comes out first. Correct.
        *   Tie-breaking: If priorities are equal (e.g., both 0), we need FIFO.
        *   We can add a global counter/incrementing index to the heap item. `( -priority, sequence, job_id, job_data )`.
        *   Since `sequence` increases, smaller `sequence` comes out first for same priority.

3.  **Drafting the Code:**
    *   Class `JobQueue`.
    *   `__init__`: Initialize heap and sequence counter.
    *   `add_job(self, job_name, job_data, priority=0)`:
        *   Increment sequence.
        *   Push `(-priority, sequence, job_name, job_data)` to heap.
    *   `get_next_job(self)`:
        *   Pop from heap.
        *   Return `(job_name, job_data)`.
        *   Handle empty queue (return `None`).

4.  **Refining the Heap Tuple:**
    *   `heapq` compares tuples element by element.
    *   Item: `(-priority, sequence, job_name, job_data)`
    *   Case 1: Priority 2 vs 0.
        *   Item 1: `(-2, seq1, ...)`
        *   Item 2: `(0, seq2, ...)`
        *   -2 < 0. Item 1 pops. Correct.
    *   Case 2: Priority 0 vs 0.
        *   Item 1: `(0, 1, ...)`
        *   Item 2: `(0, 2, ...)`
        *   0 == 0. 1 < 2. Item 1 pops. Correct (FIFO).

5.  **Constraints Check:**
    *   "Write only the Python code, no explanations."
    *   Need to import `heapq`.
    *   Need to define the class.
    *   Type hints should be included as per the prompt's `get_next_job` signature style.

6.  **Code Structure:**
    ```python
    import heapq
    from typing import Optional, Tuple, Dict

    class JobQueue:
        def __init__(self):
            self.heap = []
            self.counter = 0

        def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
            self.counter += 1
            # Use negative priority so higher priority (larger number) comes out first (smaller negative)
            heapq.heappush(self.heap, (-priority, self.counter, job_name, job_data))

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

7.  **Reviewing Requirements:**
    1.  `add_job` accepts optional priority (default=0). (Checked)
    2.  Higher priority processed before lower. (Checked via negation)
    3.  Same priority FIFO. (Checked via counter)
    4.  `get_next_job` returns `tuple[str, dict] | None`. (Checked)
    5.  Example logic matches.

8.  **Final Polish:**
    *   Ensure imports are present.
    *   Ensure type hints match the prompt's style (Python 3.10+ union syntax `|` is preferred based on prompt `tuple[str, dict] | None`, but standard typing `Optional` is safer for compatibility, though prompt uses `|`. I will use `|` in type hints to match the prompt's explicit signature style).
    *   Actually, looking closely at the prompt: `get_next_job() -> tuple[str, dict] | None`. This is valid Python 3.10+ syntax. I will use that.
    *   Wait, standard `heapq` pushes tuples. If `job_name` is a string and `job_data` is a dict, comparing them if priority and counter are equal is fine, though not strictly necessary for logic, but it ensures stability if counters collide (they won't).
    *   Wait, one edge case: If I use `job_name` as the 3rd element, and two jobs have same priority and same counter (impossible due to increment), it works. If I didn't use a counter, I'd need to compare `job_name` or `job_data`. But I am using a counter.
    *   The prompt asks for `tuple[str, dict]`.
    *   Let's double check the priority logic.
    *   Priority 2 (Critical) > Priority 1 (High) > Priority 0 (Normal).
    *   Heap is min-heap.
    *   Store `-priority`.
    *   -2 < -1 < 0.
    *   So -2 pops first. Correct.

9.  **Constructing Final Output:**
    Just the code.

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

class JobQueue:
    def __init__(self):
        self.heap = []
        self.sequence = 0

    def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
        self.sequence += 1
        # Store as (-priority, sequence, job_name, job_data)
        # Higher priority (e.g., 2) becomes -2, which is smaller than 0, so it pops first
        heapq.heappush(self.heap, (-priority, self.sequence, job_name, job_data))

    def get_next_job(self) -> Optional[Tuple[str, dict]]:
        if not self.heap:
            return None
        _, _, job_name, job_data = heapq.heappop(self.heap)
        return (job_name, job_data)
```
Wait, the prompt says `tuple[str, dict] | None`. In Python 3.9+ `dict` is genericable. In 3.10+ `|` is valid. I will use `|` to match the prompt's hint exactly.

One detail: `job_data` in `add_job` signature. The prompt example: `queue.add_job("normal", {"type": "normal"}, priority=0)`.
So `job_name` is the first arg, `job_data` is the second.
Prompt: `get_next_job() -> tuple[str, dict] | None`.

Let's write it.

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

class JobQueue:
    def __init__(self):
        self.heap = []
        self.counter = 0

    def add_job(self, job_name: str, job_data: Dict, priority: int = 0) -> None:
        self.counter += 1
        # Negate priority so higher numbers (higher priority) appear smaller in min-heap
        heapq.heappush(self.heap, (-priority, self.counter, job_name, job_data))

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self.heap:
            return None
        _, _, job_name, job_data = heapq.heappop(self.heap)
        return (job_name, job_data)
```
Wait, `Dict` vs `dict`. The prompt uses `dict` in the return type hint `tuple[str, dict] | None`. I should stick to `dict` for consistency with the prompt's style. Also `Tuple` vs `tuple`. The prompt uses `tuple`. I will use `tuple`.

Revised Code:
```python
import heapq

class JobQueue:
    def __init__(self):
        self.heap = []
        self.counter = 0

    def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
        self.counter += 1
        heapq.heappush(self.heap, (-priority, self.counter, job_name, job_data))

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self.heap:
            return None
        _, _, job_name, job_data = heapq.heappop(self.heap)
        return (job_name, job_data)
```
Wait, `heapq` requires the elements to be comparable. Tuples are compared element-wise.
1. `-priority`: Determines priority. Higher priority (e.g. 2) -> -2. Lower (0) -> 0. -2 < 0. Correct.
2. `self.counter`: Determines FIFO. Smaller counter (earlier) -> pops first. Correct.
3. `job_name`, `job_data`: Tie breakers if needed (though counter is unique).

Looks good.

One final check on the prompt: "Write only the Python code, no explanations."
I will output the code block.

One minor thing: `tuple[str, dict] | None` is Python 3.10+. If the environment is older, `Optional[Tuple[str, dict]]` is better. However, the prompt explicitly used `tuple[str, dict] | None` in the requirement description. I should follow that syntax.

Also, `add_job` signature in prompt example: `add_job("normal", {"type": "normal"}, priority=0)`.
So `job_name` is positional, `job_data` is positional, `priority` is keyword.
My code: `add_job(self, job_name: str, job_data: dict, priority: int = 0)`.
Matches.

One more check: "Higher priority jobs should be processed before lower priority jobs".
If priority 2 is Critical and 0 is Normal.
2 > 0.
Heap stores -2 and 0.
-2 < 0.
heappop returns -2 first.
So 2 is processed before 0.
This matches "Higher priority jobs processed before lower".

Wait, "Priority levels: 0: Normal, 1: High, 2: Critical".
Usually Critical > High > Normal.
So 2 > 1 > 0.
My logic holds.

Final check on imports. `heapq` is needed.
I will remove type hints if I want to be super safe, but the prompt implies typing with `tuple[str, dict] | None`. I will include them.

Wait, `job_data` in the `add_job` call in the example is a dict.
`queue.add_job("normal", {"type": "normal"}, priority=0)`
So `job_name` is string, `job_data` is dict.
My code reflects this.

One nuance: "Write only the Python code". I should not include markdown code fences if strictly interpreted, but usually, a code block is expected. I'll provide a standard markdown code block.
Actually, "Write only the Python code" often means raw text or within a block. I will use a code block for clarity.

Wait, `tuple[str, dict]` requires Python 3.9+ for lowercase generics. `|` requires 3.10+. I will assume a reasonably modern Python version based on the prompt's syntax.

Final check on `get_next_job` return.
The prompt says: `get_next_job() -> tuple[str, dict] | None`.
My code: `def get_next_job(self) -> tuple[str, dict] | None:`.
Matches.

One detail: `job_data` in the tuple return.
`return (job_name, job_data)`
Matches `tuple[str, dict]`.

Ready.

```python
import heapq

class JobQueue:
    def __init__(self):
        self.heap = []
        self.counter = 0

    def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
        self.counter += 1
        heapq.heappush(self.heap, (-priority, self.counter, job_name, job_data))

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self.heap:
            return None
        _, _, job_name, job_data = heapq.heappop(self.heap)
        return (job_name, job_data)
```
Wait, I should check if `job_data` is required to be `dict`. The prompt says `job_data: dict` in `add_job` logic implicitly by example. I will keep `dict`.

One small optimization: I don't need `Optional` import if I use `| None`.
I will use standard typing imports if needed, but `tuple` and `dict` are built-ins.
I'll stick to minimal imports. `heapq` is necessary.

Wait, one edge case: `job_data` might not be a dict in a general scenario, but the prompt specifies `dict`.
I will follow the prompt.

One more check: `get_next_job` return type hint `tuple[str, dict] | None`.
Python version compatibility:
`tuple` is a built-in.
`|` for union types was added in 3.10.
I will use `|` as requested.

Final check on FIFO.
If `counter` is unique for every call, and heap compares tuples, the second element breaks ties on the first element (priority).
Since `counter` increases monotonically, smaller `counter` comes first.
This ensures FIFO for same priority.

Looks solid.

```python
import heapq

class JobQueue:
    def __init__(self):
        self.heap = []
        self.counter = 0

    def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
        self.counter += 1
        heapq.heappush(self.heap, (-priority, self.counter, job_name, job_data))

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self.heap:
            return None
        _, _, job_name, job_data = heapq.heappop(self.heap)
        return (job_name, job_data)
```
Double check the prompt example:
`queue.add_job("normal", {"type": "normal"}, priority=0)`
My signature: `add_job(self, job_name: str, job_data: dict, priority: int = 0)`
Matches.
`queue.add_job("critical", {"type": "critical"}, priority=2)`
Matches.
`job = queue.get_next_job()`
My signature: `get_next_job(self) -> tuple[str, dict] | None`
Matches.

I will remove the type hints if I want to be strictly compliant with "no explanations" but usually type hints are part of the code. The prompt explicitly showed type hints in the requirement section (`get_next_job() -> tuple[str, dict] | None`). So I should include them.

One thing: `job_data` in `add_job` is passed as `{"type": "critical"}`.
In `get_next_job`, it returns the reference to the same dict object.
This is fine.

I will write the code now.

```python
import heapq

class JobQueue:
    def __init__(self):
        self.heap = []
        self.counter = 0

    def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
        self.counter += 1
        heapq.heappush(self.heap, (-priority, self.counter, job_name, job_data))

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self.heap:
            return None
        _, _, job_name, job_data = heapq.heappop(self.heap)
        return (job_name, job_data)
```
Wait, I should check if `dict` is valid in type hint without `typing`. Yes, in Python 3.9+ `dict` is generic. In older Python `Dict` from `typing` is needed. The prompt uses `dict` in `tuple[str, dict]`. I will use `dict`.

Final check.
Prompt: `get_next_job() -> tuple[str, dict] | None`
My code: `def get_next_job(self) -> tuple[str, dict] | None:`
Prompt: `add_job` to accept optional priority parameter (default=0).
My code: `def add_job(self, job_name: str, job_data: dict, priority: int = 0)`
Prompt: Higher priority jobs processed before lower.
My code: `-priority` ensures higher integer priority pops first in min-heap.
Prompt: Same priority FIFO.
My code: `self.counter` ensures insertion order.

Seems complete.

One minor detail: `heapq` is a module, `heappush` and `heappop` are functions.
I am using `heapq.heappush` and `heapq.heappop`.
This is correct.

I will formulate the response.

```python
import heapq

class JobQueue:
    def __init__(self):
        self.heap = []
        self.counter = 0

    def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
        self.counter += 1
        heapq.heappush(self.heap, (-priority, self.counter, job_name, job_data))

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self.heap:
            return None
        _, _, job_name, job_data = heapq.heappop(self.heap)
        return (job_name, job_data)
```
Wait, I should ensure I don't use `self` in the `add_job` call in the example logic inside my head, but the implementation must include `self`.
Yes, `def add_job(self, ...)` is correct.

One last check on the `tuple[str, dict]` vs `Tuple[str, dict]`.
The prompt uses `tuple` (lowercase). I will use `tuple` (lowercase).
This implies Python 3.9+.
The prompt uses `|` for union. This implies Python 3.10+.
I will follow the prompt's type hint style exactly.

Wait, `job_data` in `add_job` signature.
The prompt example: `queue.add_job("normal", {"type": "normal"}, priority=0)`.
This implies `job_name` is the first argument, `job_data` is the second.
My signature: `add_job(self, job_name: str, job_data: dict, priority: int = 0)`.
This matches.

Okay, generating response.

</reasoning_content>

<content>
```python
import heapq

class JobQueue:
    def __init__(self):
        self.heap = []
        self.counter = 0

    def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:
        self.counter += 1
        heapq.heappush(self.heap, (-priority, self.counter, job_name, job_data))

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