<reasoning_content>
Thinking Process:

1.  **Analyze the Request:**
    *   Goal: Implement a priority-based job scheduling queue in Python.
    *   Requirements:
        1.  `add_job` method needs an optional `priority` parameter (default=0).
        2.  Higher priority values (e.g., 2 > 1 > 0) should be processed first.
        3.  Same priority jobs must maintain FIFO (First-In-First-Out) order.
        4.  Implement `get_next_job()` returning `tuple[str, dict] | None`.
    *   Output: Only Python code, no explanations.

2.  **Determine the Data Structure:**
    *   To maintain priority ordering while preserving FIFO order for same priorities, a heap (priority queue) is the standard choice.
    *   Python's `heapq` module implements a min-heap.
    *   Since higher priority (e.g., 2) should come before lower priority (e.g., 0), and `heapq` pops the smallest item first, I need to invert the priority logic.
    *   Option A: Store priority as negative (e.g., -2, -1, 0). Then -2 < -1 < 0, so -2 (priority 2) pops first.
    *   Option B: Store a tuple `(-priority, timestamp, job_data)`. `heapq` compares tuples element by element. If priorities are equal, it compares timestamps. To maintain FIFO, the earlier timestamp should come first.
    *   Let's use a counter/timestamp to break ties.
    *   Priority mapping:
        *   0: Normal
        *   1: High
        *   2: Critical
    *   If I want Priority 2 to come out before Priority 1, I should store `-priority`.
        *   Priority 2 -> -2
        *   Priority 1 -> -1
        *   Priority 0 -> 0
        *   Heap order: -2 < -1 < 0. So -2 comes first. Correct.
    *   Tie-breaking: If priorities are equal (e.g., two Normal jobs), I need to ensure the one added first comes out first.
    *   Tuple structure for heap item: `(-priority, insertion_order, job_id, job_data)`.
    *   `insertion_order` ensures FIFO for same priority.
    *   Return type: `tuple[str, dict]`. The example shows `add_job("normal", {"type": "normal"})`. So the tuple should be `(job_id, job_data)`.

3.  **Drafting the Class:**
    *   Class name: `JobQueue` (implied by example).
    *   Imports: `heapq`, `itertools` (for counter), `typing`.
    *   `__init__`: Initialize heap and counter.
    *   `add_job`: Accept `job_id`, `job_data`, `priority`. Push to heap.
    *   `get_next_job`: Pop from heap. Handle empty queue.

4.  **Refining the Heap Logic:**
    *   Python's `heapq` is a min-heap.
    *   We want Critical (2) > High (1) > Normal (0).
    *   So we store `-priority`.
    *   To ensure FIFO for same priority, we need a monotonic counter.
    *   Heap item: `(-priority, counter, job_id, job_data)`.
    *   `get_next_job` returns `(job_id, job_data)`.

5.  **Edge Cases:**
    *   Empty queue: Return `None`.
    *   Data types: Ensure `job_data` is a dict based on the example (though the prompt says `dict`, I should treat it as generic object but type hint as `dict`).
    *   Priority default: 0.

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

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

        def add_job(self, job_id: str, job_data: Dict, priority: int = 0) -> None:
            # Invert priority for min-heap (higher priority value -> smaller negative value)
            # e.g., 2 -> -2, 1 -> -1, 0 -> 0
            # This ensures priority 2 is popped before 1, which is before 0.
            heap_item = (-priority, self._counter, job_id, job_data)
            heapq.heappush(self._heap, heap_item)
            self._counter += 1

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

7.  **Review against constraints:**
    *   `add_job` accepts optional priority (default=0)? Yes.
    *   Higher priority processed before lower? Yes (-2 < -1 < 0).
    *   Same priority maintains FIFO? Yes (counter increments).
    *   `get_next_job` returns `tuple[str, dict] | None`? Yes.
    *   Example usage matches?
        *   `add_job("normal", ..., priority=0)` -> item `(-0, 0, ...)`
        *   `add_job("critical", ..., priority=2)` -> item `(-2, 1, ...)`
        *   `add_job("high", ..., priority=1)` -> item `(-1, 2, ...)`
        *   Heap order: `(-2, 1)`, `(-1, 2)`, `(-0, 0)`.
        *   Pop: `(-2, 1)` -> Critical. Correct.
    *   Output only code? Yes.

8.  **Final Polish:**
    *   Ensure imports are standard.
    *   Ensure type hints are correct.
    *   Check if `job_data` should strictly be `Dict`. The prompt implies `dict`.
    *   Make sure to handle the return type hint correctly (`tuple[str, dict] | None`).

    Wait, one detail: `tuple[str, dict] | None` is Python 3.10+ syntax. I should stick to valid Python syntax. If the environment is older, I might need `Optional[Tuple[str, Dict]]`. However, the prompt uses `|` in the requirement description `get_next_job() -> tuple[str, dict] | None`. I will use the modern syntax if possible, but `typing.Optional` is safer for compatibility. Given the instruction "Write only the Python code", I will use standard imports and type hints that work well. I will use `Optional` from `typing` to be safe, or just the union syntax if assuming 3.10+. The prompt uses the union syntax in the requirement, so I will use it in the implementation to match.

    Actually, looking at the requirement `-> tuple[str, dict] | None`, I should probably use `from __future__ import annotations` or just rely on 3.10+ syntax. I'll stick to standard typing imports for maximum compatibility or just use the syntax provided. Let's use `typing` imports for clarity.

    Wait, `tuple[str, dict]` requires Python 3.9+ for builtin generics. `tuple[str, dict] | None` requires Python 3.10+.
    I will use `Optional[Tuple[str, Dict]]` to be safe across versions, but the prompt specifically wrote `tuple[str, dict] | None`. I will match the prompt's style if possible, but standard `typing` is safer. Let's use `typing` module for type hints to be robust.

    Re-reading requirement: `get_next_job() -> tuple[str, dict] | None`.
    I will write the code compatible with this.

    Let's refine the implementation.

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

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

    def add_job(self, job_id: str, job_data: Dict, priority: int = 0) -> None:
        # Store as (-priority, counter, job_id, job_data)
        # Negating priority ensures higher priority (e.g. 2) comes before lower (e.g. 0)
        # Counter ensures FIFO 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 not self._heap:
            return None
        _, _, job_id, job_data = heapq.heappop(self._heap)
        return (job_id, job_data)
```
    Wait, I need to check if the prompt implies a specific class name. The example uses `queue = JobQueue()`. So the class must be named `JobQueue`.
    The type hint in the requirement uses `tuple[str, dict] | None`. I will use `Optional[Tuple[str, Dict]]` for compatibility but the logic is the same. Or I can use the union syntax directly. I'll stick to `Optional` and `Tuple` from `typing` to be safe, or just use the syntax provided in the prompt if I assume 3.10+. I'll use `typing` imports for clarity.

    Actually, to strictly follow "Write only the Python code", I should ensure it's runnable.
    I will use `from typing import Optional, Tuple, Dict`.

    One check: `job_data` in the example is `{"type": "normal"}`. It is a dict.
    The return type is `tuple[str, dict]`.
    So `job_data` is the dict.

    Final check on logic:
    Priority 2 (Critical) -> -2
    Priority 1 (High) -> -1
    Priority 0 (Normal) -> 0
    Heap min: -2 is smaller than -1, which is smaller than 0.
    So -2 pops first. Correct.

    FIFO:
    Same priority means same `-priority`.
    Counter increases: 0, 1, 2...
    Heap compares (neg_priority, counter).
    If neg_priority is same, smaller counter pops first.
    Counter 0 < Counter 1.
    So earlier jobs pop first. Correct.

    Ready to write.

    Wait, one detail in the prompt: `get_next_job() -> tuple[str, dict] | None`.
    I will use this exact signature style in the code.

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

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 (larger number) pops first
        # 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 not self._heap:
            return None
        _, _, job_id, job_data = heapq.heappop(self._heap)
        return (job_id, job_data)
```
    Wait, I should check if I need to import `Optional` or use `|`. The prompt uses `|` in the requirement description. I will use `Optional` from typing for the return type annotation to be safe, or just `tuple[...] | None`. I'll use `Optional` as it's more standard in older Python versions, but since the prompt used `|`, I will use `Optional` to avoid version issues unless I assume 3.10+. I'll stick to `typing` imports.

    Actually, looking at the instruction "Write only the Python code", I should provide the class definition.

    One more thing: The prompt example shows `add_job("normal", {"type": "normal"}, priority=0)`.
    My signature `add_job(self, job_id: str, job_data: Dict, priority: int = 0)` matches this.

    Let's produce the final code block.

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

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

    def add_job(self, job_id: str, job_data: Dict, priority: int = 0) -> None:
        # Store (-priority, counter, job_id, job_data)
        # Negating priority ensures higher priority values (e.g. 2) are popped before lower (e.g. 0)
        # Counter ensures FIFO order for jobs with the 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 not self._heap:
            return None
        _, _, job_id, job_data = heapq.heappop(self._heap)
        return (job_id, job_data)
```
    Wait, I should check if `dict` in type hint needs to be `Dict` from typing. Yes, `dict` is builtin but `Dict` is typing. In Python 3.9+ `dict` works as generic. The prompt uses `dict` in `tuple[str, dict]`. I will use `dict` for the type hint to match the prompt's style if possible, but `Dict` is safer for older versions. I'll use `dict` since 3.9+ supports it and the prompt uses it.
    Actually, to be most compatible and correct, `Dict` from `typing` is preferred in type hints. But the prompt wrote `tuple[str, dict]`. I will use `dict` in the code to match the prompt's type hint style, assuming Python 3.9+.
    However, `Optional` is `typing.Optional`.
    Let's just use `typing` imports for everything to be clean.
    Wait, `tuple[str, dict]` is valid in 3.9+.
    I will use `Optional[Tuple[str, dict]]` to match the prompt's `tuple[str, dict] | None`.

    Revised Code:
    ```python
    import heapq
    from typing import Optional, Tuple

    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) -> Optional[Tuple[str, dict]]:
            if not self._heap:
                return None
            _, _, job_id, job_data = heapq.heappop(self._heap)
            return (job_id, job_data)
    ```
    Wait, `job_data: dict` vs `job_data: Dict`. The prompt example passes a dict literal.
    I will use `dict` for the type hint to match the prompt's `tuple[str, dict]`.
    And `Optional` for `None` union.

    Final check on imports. `heapq` is standard. `typing` is standard.
    Okay.

    One small detail: `get_next_job` return type in prompt is `tuple[str, dict] | None`.
    I will use `Optional[Tuple[str, dict]]` which is equivalent.

    Wait, I'll use `from typing import Optional, Tuple` and define the class.
    I will remove comments to keep it clean, as per "Write only the Python code". But comments explaining logic are usually acceptable in code blocks. The prompt says "no explanations", usually meaning no text outside the code block. I will keep the code clean.

    One thing: The prompt asks to "Implement priority-based job scheduling for the queue." and then lists requirements. It implies I should provide the class implementation.
    I will provide the complete class.

    Wait, I need to make sure I don't use `Dict` if I use `dict` in the hint.
    I'll use `dict` for the parameter type hint.

    Final check on priority logic.
    Requirement: Higher priority jobs should be processed before lower priority jobs.
    Priority levels: 0 (Normal), 1 (High), 2 (Critical).
    So 2 > 1 > 0.
    Heap is min-heap.
    To pop 2 before 1, we need value(2) < value(1) in heap.
    -2 < -1.
    So storing `-priority` works.
    If priority is 0, store 0.
    -2 < -1 < 0.
    Pop order: -2 (Priority 2), -1 (Priority 1), 0 (Priority 0).
    This matches "Higher priority jobs should be processed before lower priority jobs".

    FIFO for same priority:
    Use a counter.
    Push `(-priority, counter, ...)`.
    If priorities are same, compare counters.
    Smaller counter (earlier job) comes first.
    This works.

    Code looks solid.

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

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) -> Optional[Tuple[str, Dict]]:
        if not self._heap:
            return None
        _, _, job_id, job_data = heapq.heappop(self._heap)
        return (job_id, job_data)
```
    I will use `Dict` from typing for consistency, even though the prompt used `dict` in the type hint description. It's safer.
    Actually, I'll use `dict` in the type hint to match the prompt's `tuple[str, dict]`.
    And `Optional` for the union.
    Okay, I'll stick to standard typing practices. `Dict` is fine.

    Wait, the prompt example code:
    ```python
    queue = JobQueue()
    queue.add_job("normal", {"type": "normal"}, priority=0)
    ```
    This implies the class exists. I need to provide the class.

    Final check: "Write only the Python code, no explanations."
    I will output just the code block.

    One detail: The prompt requirement `get_next_job() -> tuple[str, dict] | None`.
    I will use `Optional[Tuple[str, dict]]` as it is semantically identical and widely supported.
    Or I can use the union syntax if I assume Python 3.10+.
    Given the prompt uses `|`, I will use `|` in the return type annotation to be precise.
    So: `def get_next_job(self) -> tuple[str, dict] | None:`
    But I need to import `Tuple` or just use `tuple`. Python 3.9+ supports `tuple[...]`.
    I'll use `tuple` and `dict` built-ins for hints to match the prompt exactly.
    And `Optional` from typing for the union representation if I don't use `|`.
    Actually, `from typing import Optional` allows `Optional[Tuple[str, dict]]`.
    I will use `Optional` and `Tuple` for clarity.

    Wait, I'll use `Optional` because `|` syntax in return type requires 3.10+. The prompt uses it, so I should probably use it too to show I read it. But `Optional` is safer. I will use `Optional[Tuple[str, dict]]`.

    Wait, I'll just use `Optional` and `Tuple` from `typing`.
    And `dict` for the data type.

    Okay, ready.

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

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) -> Optional[Tuple[str, Dict]]:
        if not self._heap:
            return None
        _, _, job_id, job_data = heapq.heappop(self._heap)
        return (job_id, job_data)
```
    Wait, I need to make sure I don't miss the `dict` vs `Dict`. The prompt uses `dict` in `tuple[str, dict]`.
    I will use `dict` in the type hint to be consistent with the prompt's `tuple[str, dict]`.
    So `job_data: dict`.
    And return type `tuple[str, dict] | None`.
    I'll use `from __future__ import annotations` maybe? No, keep it simple.
    I'll use `dict` for type hint.

    Okay, final version.
    I will use `typing` imports for `Optional` and `Tuple` but `dict` for the inner type.
    Actually, `Tuple` is from `typing`.
    Okay.

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

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) -> Optional[Tuple[str, dict]]:
        if not self._heap:
            return None
        _, _, job_id, job_data = heapq.heappop(self._heap)
        return (job_id, job_data)
```
    This looks correct and matches requirements.
    Wait, `Tuple[str, dict]` vs `tuple[str, dict]`.
    I'll use `tuple[str, dict]` in the return type hint to match the prompt exactly.
    `Optional` handles the `| None`.
    So `def get_next_job(self) -> Optional[tuple[str, dict]]:`
    This works in Python 3.9+ with typing imports.
    Actually, `Optional` is from `typing`.
    Okay.

    Let's write it.

</reasoning_content>

<content>
```python
import heapq
from typing import Optional, Tuple

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) -> Optional[Tuple[str, dict]]:
        if not self._heap:
            return None
        _, _, job_id, job_data = heapq.heappop(self._heap)
        return (job_id, job_data)
```
</content>