<reasoning_content>
Thinking Process:

1.  **Analyze the Request:**
    *   Goal: Implement a priority-based job scheduling queue in Python.
    *   Requirements:
        1.  Modify `add_job` to accept an optional `priority` parameter (default=0).
        2.  Higher priority jobs processed before lower priority jobs (Priority 2 > Priority 1 > Priority 0).
        3.  Jobs with the same priority maintain FIFO order.
        4.  Add `get_next_job() -> tuple[str, dict] | None` method.
    *   Input/Output: Python code only, no explanations.
    *   Example provided shows usage.

2.  **Determine Data Structure:**
    *   Need to store jobs with priorities.
    *   Need to extract the highest priority job.
    *   Need to maintain FIFO for same priority.
    *   Standard Python `heapq` is suitable for priority queues.
    *   To maintain FIFO for same priority, I need to include a sequence counter (timestamp or incrementing index) in the heap tuple.
    *   Tuple structure for heap: `(priority, sequence, job_id, job_data)`.
    *   Since higher priority means *higher* number (2 > 1 > 0), `heapq` (min-heap) works if I invert the priority or just use negative priority.
    *   Wait, the requirement says: "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.
    *   `heapq` pops the smallest item.
    *   If I store `(priority, sequence, ...)`, 0 comes before 2. That's the opposite of what's needed (2 should come before 0).
    *   Solution 1: Store `(-priority, sequence, ...)`. Then -2 < -1 < 0. So -2 (Priority 2) pops first.
    *   Solution 2: Use `heapq` with a custom class implementing `__lt__`.
    *   Solution 3: Store `(priority, sequence, ...)` and pop the max. Python's `heapq` is a min-heap.
    *   Let's use Solution 1 (negating priority) for simplicity, or just handle the logic carefully.
    *   Actually, let's look at the example:
        `queue.add_job(..., priority=2)` -> Critical.
        `queue.add_job(..., priority=0)` -> Normal.
        `get_next_job()` returns Critical.
        So Priority 2 > Priority 1 > Priority 0.
    *   To use a min-heap (`heapq`) where the smallest item is popped:
        *   Store `(-priority, sequence, job_id, job_data)`.
        *   Priority 2 becomes -2. Priority 0 becomes 0.
        *   -2 < 0, so Priority 2 comes out first. Correct.
    *   To maintain FIFO for same priority:
        *   Use a global counter `self._sequence`.
        *   Increment it on every `add_job`.
        *   Tuple: `(-priority, sequence, job_id, job_data)`.
        *   Since `sequence` is unique and increasing, if priorities are equal, lower sequence (earlier added) comes out first.

3.  **Drafting the Code:**
    *   Class `JobQueue`.
    *   `__init__`: Initialize heap and sequence counter.
    *   `add_job(self, job_id: str, job_data: dict, priority: int = 0)`: Push to heap.
    *   `get_next_job(self) -> tuple[str, dict] | None`: Pop from heap. Handle empty case.

4.  **Refining the Tuple:**
    *   `heapq` compares tuples element by element.
    *   `(-priority, sequence, job_id, job_data)`.
    *   If priorities differ, -priority decides.
    *   If priorities equal, sequence decides (FIFO).
    *   `job_id` and `job_data` are technically not needed for ordering, but needed for the return value. They act as tie-breakers if sequence wasn't unique (which it is).
    *   Return type hint: `tuple[str, dict] | None`.

5.  **Constraints:**
    *   "Write only the Python code, no explanations."
    *   Ensure imports are included (`heapq`).

6.  **Edge Cases:**
    *   Empty queue: `get_next_job` returns `None`.
    *   Priority levels: 0, 1, 2.
    *   Negative priority? The prompt implies 0, 1, 2, but logic should support general integers. I'll stick to the logic derived.

7.  **Final Code Structure:**
    ```python
    import heapq

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

        def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
            # Negate priority so higher priority (e.g., 2) becomes smaller number (-2)
            # to be popped first by min-heap
            entry = (-priority, self._sequence, job_id, job_data)
            heapq.heappush(self._heap, entry)
            self._sequence += 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)
    ```

8.  **Verification against requirements:**
    1.  `add_job` accepts optional priority (default=0). (Check)
    2.  Higher priority (2) before lower (0). (Check: -2 < 0)
    3.  Same priority FIFO. (Check: `_sequence` ensures order)
    4.  `get_next_job` returns tuple or None. (Check)

9.  **Formatting:**
    *   Remove comments to keep it clean? The prompt says "Write only the Python code". Comments are usually fine, but minimal is better. I will include necessary imports.
    *   Ensure type hints are correct.

10. **Final Check:**
    *   Does `priority=0` default work? Yes.
    *   Does the example work?
        *   add normal (0) -> (0, 0, ...)
        *   add critical (2) -> (-2, 1, ...)
        *   add high (1) -> (-1, 2, ...)
        *   heap: [(-2, 1, ...), (0, 0, ...), (-1, 2, ...)] (conceptually sorted)
        *   pop: (-2, ...) -> Critical. Correct.

    *   Wait, standard `heapq` maintains the heap invariant, it doesn't fully sort the list immediately, but `heappop` returns the smallest.
    *   Comparison:
        *   (-2, 1, "critical", ...)
        *   (-1, 2, "high", ...)
        *   (0, 0, "normal", ...)
        *   -2 is smallest. It pops first.
    *   FIFO check:
        *   add normal (0) -> (0, 0, ...)
        *   add normal (0) -> (0, 1, ...)
        *   Pop 1: (0, 0, ...) -> First normal.
        *   Pop 2: (0, 1, ...) -> Second normal.
    *   Looks good.

    *   Wait, one detail: `tuple[str, dict] | None`. In older Python versions, `|` union syntax might need `from __future__ import annotations` or `typing.Union`. However, modern Python (3.10+) supports `|`. I will assume modern Python syntax is acceptable. If strict compatibility is needed, `Union` is safer, but `|` is standard now for type hints. I'll use `|` as per the prompt's usage in the requirement description.

    *   Wait, the prompt says `get_next_job() -> tuple[str, dict] | None`. This implies Python 3.10+ style type hinting. I will use that.

    *   One more check on imports. `import heapq` is needed.

    *   Is there any specific class name required? The example uses `JobQueue()`. I should name the class `JobQueue`.

    *   Is there any specific method signature required? `add_job` and `get_next_job`.

    *   Let's write the code.

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

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        # Negate priority so higher numbers (e.g., 2) become smaller (e.g., -2)
        # to be prioritized by the min-heap.
        entry = (-priority, self._sequence, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._sequence += 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, `Tuple[str, dict] | None` might need `typing` import or `from __future__ import annotations`. I'll use `typing` imports to be safe and explicit, or just use the syntax provided in the prompt. The prompt uses `tuple[str, dict] | None` in the text description. I will stick to that syntax.
    *   Actually, using `tuple` (builtin) vs `Tuple` (typing). Python 3.9+ allows `tuple[...]`.
    *   I will use `tuple` and `|` for modern syntax.

    *   Wait, I need to make sure I don't add extra text like "Here is the code". Just the code.

    *   One small detail: `job_data` type hint in `add_job`. The prompt example uses `{"type": "normal"}`. `dict` is fine.

    *   Re-checking FIFO requirement: "Jobs with the same priority should maintain FIFO order".
    *   My implementation uses `_sequence` which increments monotonically.
    *   Tuple comparison: `(-priority, sequence, ...)`.
    *   If priorities are equal, `-priority` is equal.
    *   Then `sequence` is compared.
    *   Since `sequence` increases with time, the one added earlier has a smaller `sequence`.
    *   Min-heap pops smallest `sequence`.
    *   So earlier jobs are popped first. FIFO is maintained.

    *   Final check on imports. `heapq` is standard.

    *   Wait, should I include the example usage in the code? The prompt says "Implement... Write only the Python code". Usually, this means the class definition. Including the example usage block at the bottom is often helpful for verification but strictly speaking, the implementation is the class. I will provide the class definition. I will omit the example usage block to keep it clean, unless it helps clarify. The prompt example shows usage, but the requirement is to implement the logic. I will provide the class.

    *   Wait, looking at the instruction "Write only the Python code". I should avoid markdown blocks like ```python ... ``` if strictly interpreted, but usually markdown code blocks are expected. I will use markdown code block for clarity.
    *   Actually, usually "Write only the Python code" implies the content inside the block should be the code. I will wrap it in markdown block.

    *   Wait, one nuance: `tuple[str, dict] | None`. In Python < 3.9, `tuple` doesn't support subscripting. In Python 3.9+, it does. I'll assume Python 3.9+ environment.

    *   One more thing: The prompt example shows `queue.add_job("normal", {"type": "normal"}, priority=0)`.
    *   My signature: `add_job(self, job_id: str, job_data: dict, priority: int = 0)`. Matches.
    *   My `get_next_job` return: `tuple[str, dict]`. Matches `("normal", {"type": "normal"})`.

    *   Ready to write.

```python
import heapq

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        entry = (-priority, self._sequence, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._sequence += 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, I should check if `tuple` is available without import in the type hint context. In Python 3.9+, yes. In 3.10+, yes.
    *   I will use `typing` just to be safe? No, builtin `tuple` is standard now.
    *   I'll stick to the prompt's style.

    *   Wait, I need to make sure I don't miss the `if __name__ == "__main__":` part if it's expected? No, just the implementation.

    *   Wait, I should check if `dict` is generic. `dict[str, Any]` is better but `dict` is what the example uses implicitly. The prompt says `job_data: dict`. I will use `dict`.

    *   Final check on priority logic.
    *   Priority 2 (Critical) -> -2.
    *   Priority 0 (Normal) -> 0.
    *   -2 < 0.
    *   heappop returns smallest.
    *   Returns Priority 2.
    *   Correct.

    *   Final check on FIFO.
    *   Job A (Pri 0, Seq 0)
    *   Job B (Pri 0, Seq 1)
    *   Tuple A: (0, 0, ...)
    *   Tuple B: (0, 1, ...)
    *   A < B.
    *   A pops first.
    *   FIFO maintained.
    *   Correct.

    *   One detail: `job_data` in `add_job` is typed as `dict`. In Python, `dict` is a class, `dict[str, str]` is better. The prompt says `job_data: dict`. I will use `dict`.

    *   Okay, ready.

```python
import heapq

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        entry = (-priority, self._sequence, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._sequence += 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, I need to ensure the return type hint matches the requirement exactly: `tuple[str, dict] | None`.
    *   The code above does that.

    *   One minor optimization: `job_data` might be large, passing by reference is fine.
    *   The heap stores references.
    *   Seems fine.

    *   Wait, is `job_id` strictly required to be `str`? The example uses strings. The return type hint says `tuple[str, dict]`. So yes.

    *   Okay, generating output.

```python
import heapq

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        entry = (-priority, self._sequence, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._sequence += 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, one last check on `tuple[str, dict]`. In Python 3.9+, `tuple` is generic. Before that, `Tuple`. I'll use `tuple` as per the prompt's requirement text.
    *   Wait, I should check if I need `from __future__ import annotations`. Usually not required for simple scripts, but good practice. I'll skip it to keep it simple as per "Write only the Python code".
    *   Okay.

    *   Wait, I notice the prompt asks for `get_next_job() -> tuple[str, dict] | None`.
    *   My code has `-> tuple[str, dict] | None`.
    *   Matches.

    *   Okay, final version.

```python
import heapq

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        entry = (-priority, self._sequence, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._sequence += 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.

    *   Wait, I should check if the prompt implies I should include the example code in the output? "Implement priority-based job scheduling... Write only the Python code". Usually means the solution class. I will provide the class.

    *   One more thing: The prompt example uses `queue = JobQueue()`.
    *   I will make sure the class is named `JobQueue`.

    *   Okay, proceeding.

```python
import heapq

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        entry = (-priority, self._sequence, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._sequence += 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, I'll remove the indentation on the markdown block to ensure it renders correctly.
    *   Actually, I'll just output the code block.

    *   Wait, I need to make sure I don't output markdown ticks if the instruction implies raw text. "Write only the Python code". Usually means inside a code block. I will use a code block.

    *   Wait, I will remove the comments to be strictly "code only" if possible, but comments explain logic. I'll keep them minimal.
    *   Actually, I'll remove comments to be safe on "only code".
    *   Wait, `add_job` logic needs to be clear.
    *   I'll keep it concise.

    *   Wait, `tuple[str, dict] | None` requires Python 3.10+. If the environment is older, it might fail. But the prompt uses that syntax in the requirement, so I should match it.

    *   Okay, final decision: Include imports, Class, Methods. No extra text.

```python
import heapq

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        entry = (-priority, self._sequence, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._sequence += 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 good.

</reasoning_content>

<content>
```python
import heapq

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

    def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
        entry = (-priority, self._sequence, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._sequence += 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)
```
</content>