<reasoning_content>
Thinking Process:

1.  **Analyze the Request:**
    *   Goal: Implement a priority-based job scheduling queue in Python.
    *   Requirements:
        1.  `add_job` method must accept an optional `priority` parameter (default=0).
        2.  Higher priority values mean higher priority (2 > 1 > 0).
        3.  FIFO order for same priority.
        4.  `get_next_job` method returns the highest priority job as a tuple `(job_id, job_data)`.
        5.  Output: Only Python code, no explanations.

2.  **Determine Data Structure:**
    *   To maintain FIFO within the same priority, I need to preserve insertion order for items with equal priority.
    *   To sort by priority efficiently, a heap is a common choice.
    *   However, Python's `heapq` is a min-heap. If higher priority means larger number (e.g., 2 > 1 > 0), I need to negate the priority or invert the comparison logic to make it work with a min-heap.
    *   Alternatively, I can use a dictionary of queues (one queue per priority level), but a heap is generally more flexible for adding jobs dynamically.
    *   Let's check the requirement: "Higher priority jobs should be processed before lower priority jobs".
    *   Example: Priority 2 (Critical) > Priority 1 (High) > Priority 0 (Normal).
    *   Since `heapq` pops the smallest item, I should store `(-priority, insertion_order, job_id, job_data)`.
    *   `insertion_order` is needed to maintain stability (FIFO) for same priorities.

3.  **Drafting the Class:**
    *   Class name: `JobQueue` (implied by the example usage).
    *   Attributes:
        *   `_heap`: List to store jobs.
        *   `_counter`: Integer to track insertion order for stability.
    *   Methods:
        *   `__init__`: Initialize heap and counter.
        *   `add_job(job_id: str, job_data: dict, priority: int = 0)`: Push to heap.
        *   `get_next_job() -> tuple[str, dict] | None`: Pop from heap.
        *   `peek_job()` (optional, but not requested).
        *   `is_empty()` (optional, but good practice).

4.  **Refining the Heap Tuple:**
    *   Tuple structure: `(-priority, insertion_counter, job_id, job_data)`
    *   Why `-priority`? Because `heapq` pops the smallest.
        *   Priority 2 -> -2
        *   Priority 1 -> -1
        *   Priority 0 -> 0
        *   -2 < -1 < 0. So Priority 2 comes out first. Correct.
    *   Why `insertion_counter`?
        *   If priorities are equal (e.g., both 0), compare counters.
        *   Smaller counter means older job. `heapq` pops smallest. So older job comes out first. Correct.

5.  **Drafting the Code:**

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

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

       def add_job(self, job_id: str, job_data: dict, priority: int = 0) -> None:
           # Negate priority so higher priority numbers come out first (min-heap behavior)
           # Counter ensures FIFO for same priority
           entry = (-priority, self._counter, job_id, job_data)
           heapq.heappush(self._heap, entry)
           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)
   ```

6.  **Review against Constraints:**
    *   `add_job` accepts optional priority (default=0)? Yes.
    *   Higher priority processed before lower? Yes (using negation).
    *   Same priority FIFO? Yes (using counter).
    *   `get_next_job` returns tuple? Yes.
    *   No explanations, only code? Yes.

7.  **Final Polish:**
    *   Ensure imports are correct.
    *   Ensure type hints match the request (`tuple[str, dict] | None` style).
    *   The example uses `job_id` as the first argument (string) and `job_data` as the second (dict). My draft matches this.
    *   Wait, the example shows `queue.add_job("normal", {"type": "normal"}, priority=0)`.
    *   My signature: `add_job(self, job_id, job_data, priority=0)`. This matches.
    *   Return type hint `tuple[str, dict] | None` implies Python 3.10+. I should use standard typing if unsure, but the prompt uses `|` syntax in the requirement description (`tuple[str, dict] | None`), so I will use that syntax for the return type hint to match.

8.  **Constructing Final Output:**
    *   Combine imports and class definition.

    ```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:
            entry = (-priority, self._counter, job_id, job_data)
            heapq.heappush(self._heap, entry)
            self._counter += 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]` or `tuple[str, dict]`? The prompt says `tuple[str, dict] | None`. I will use `tuple` lowercase to match the prompt's type hint style exactly, assuming modern Python 3.10+ context.
    *   Actually, standard practice often prefers `typing` module imports for compatibility, but the prompt explicitly used `tuple[str, dict] | None` in the requirement. I will stick to the prompt's style for the return type hint.
    *   Wait, one edge case: `job_data` is `dict`.
    *   Let's check the imports. `heapq` is standard.

    *   Wait, I should check if the prompt implies a specific class name. The example says `queue = JobQueue()`. So the class must be named `JobQueue`.

    *   Refining the type hint to ensure compatibility and strict adherence.
    *   `get_next_job(self) -> tuple[str, dict] | None:`

    *   Wait, `job_data` in the example is `{"type": "normal"}`. It's a dict.
    *   The implementation looks solid.

    *   One minor detail: `priority` default is 0.
    *   Requirements: "Higher priority jobs should be processed before lower priority jobs".
    *   Example: 2 (Critical) > 1 (High) > 0 (Normal).
    *   My logic: `(-priority, ...)`.
    *   Priority 2 -> -2.
    *   Priority 1 -> -1.
    *   Priority 0 -> 0.
    *   Min-heap pops smallest. -2 < -1 < 0. So Priority 2 pops first. This is correct.

    *   Wait, does the prompt imply `priority` is just a level or a number?
    *   "Priority levels: 0: Normal, 1: High, 2: Critical".
    *   So higher number = higher priority.
    *   My logic holds.

    *   Final check on imports. `heapq` is needed.
    *   Final check on code style. Clean, no comments (or minimal comments if necessary, but prompt says "Write only the Python code, no explanations"). I will omit comments to be safe.

    *   Wait, I need to make sure I don't include markdown code block ticks if the instruction "Write only the Python code" implies raw text, but usually, it means inside a code block. However, to be strictly compliant with "no explanations", I will provide the code block. Wait, usually "Write only the Python code" means the output should be the code itself. I will wrap it in a markdown block for clarity as is standard for code output.
    *   Actually, often "Write only the Python code" means don't write "Here is the code:" before it. I will just provide the code block.

    *   Wait, looking at the instruction "Write only the Python code, no explanations."
    *   I will output the code inside a markdown block.

    *   Wait, one more check on the `get_next_job` return type.
    *   Requirement: `get_next_job() -> tuple[str, dict] | None`
    *   I will use that exact signature.

    *   Wait, `Dict` vs `dict`. In Python 3.9+, `dict` can be used for type hinting. The prompt uses `dict` in the example signature. I will use `dict`.

    *   Re-checking the `add_job` signature.
    *   `add_job` to accept an optional priority parameter (default=0).
    *   Example: `queue.add_job("normal", {"type": "normal"}, priority=0)`.
    *   So `job_id`, `job_data`, `priority=0`.

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

    *   Wait, I should check if `typing` is needed. The prompt uses `tuple[str, dict] | None` which is valid Python 3.10+. I don't need to import `Optional` or `Tuple` if I use built-in types, but `heapq` needs import.
    *   I will import `heapq`.

    *   One detail: "Write only the Python code". I should avoid markdown formatting if possible? No, usually code blocks are expected. I'll use a standard markdown code block.

    *   Wait, I need to make sure I don't add `if __name__ == "__main__":` unless necessary. The prompt asks for implementation. I will provide the class.

    *   Double check: "Jobs with the same priority should maintain FIFO order".
    *   My solution uses `_counter` to break ties in the heap tuple. `(-priority, counter, ...)`
    *   Since heap compares tuples element by element:
        1. Compare `-priority`.
        2. If equal, compare `counter`.
        3. Since `counter` increments with insertion, lower counter (older) comes first.
    *   This ensures FIFO for same priority. Correct.

    *   Final verification of the output format. Just the code.

    *   Wait, I should check if `job_data` needs to be copied? The example passes a dict. Modifying the returned dict might affect the original if not careful, but usually, job queues just pass references. The requirement doesn't specify deep copy. I'll stick to passing the reference as per the example signature `job_data: dict`.

    *   Let's assemble.

```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:
        entry = (-priority, self._counter, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._counter += 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 complete.

    *   One small thing: The prompt says `get_next_job() -> tuple[str, dict] | None`.
    *   I will use that exact return type annotation.
    *   I will remove comments to keep it clean.

    *   Wait, is `dict` valid in type hint without importing `Dict` from typing in older python? Yes, in 3.9+. Given the `|` union syntax, we assume 3.10+.
    *   Okay, ready.

    *   Wait, I'll check if I need to handle `job_id` and `job_data` names exactly. The example uses `"normal"` and `{"type": "normal"}`. These map to `job_id` and `job_data`.
    *   The requirement says `add_job` accepts optional priority.
    *   Okay.

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

    *   Wait, I should consider if the user wants the solution to be a snippet or a full file. "Implement...". Usually means the class definition.
    *   I will provide the class definition.

    *   One nuance: `job_data` in the example is a dict.
    *   The signature in requirement: `get_next_job() -> tuple[str, dict] | None`.
    *   I will use `dict` in the type hint.

    *   Okay, generating 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:
        entry = (-priority, self._counter, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._counter += 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. I will remove the markdown block ticks if "only python code" implies raw text, but standard interpretation is a code block. I will use a code block.
    *   Wait, "Write only the Python code". Sometimes this means no markdown ticks. But usually, it means no conversational text. I will use markdown ticks to denote code.
    *   Actually, to be safest with "only python code", I will output the code inside the block.

    *   Wait, I'll check the `add_job` parameter order.
    *   `queue.add_job("normal", {"type": "normal"}, priority=0)`
    *   This implies `job_id` is first positional, `job_data` is second positional, `priority` is keyword.
    *   My signature: `def add_job(self, job_id: str, job_data: dict, priority: int = 0)`.
    *   This matches.

    *   Okay, final check.
    *   Imports: `heapq`.
    *   Class: `JobQueue`.
    *   Methods: `__init__`, `add_job`, `get_next_job`.
    *   Logic: Heap with negated priority and counter.
    *   Return: Tuple.
    *   Priority: Higher number = higher priority (handled by negation).
    *   FIFO: Handled by counter.

    *   Ready.

```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:
        entry = (-priority, self._counter, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._counter += 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 `typing` is needed for `tuple` if I want to be strictly compliant with older python, but the prompt uses `tuple[...]` syntax which is 3.9+. I'll stick to that.
    *   Okay.

    *   Wait, one detail. `get_next_job` returns `tuple[str, dict] | None`.
    *   My code returns `(job_id, job_data)`. This is a tuple.
    *   If empty, returns `None`.
    *   Matches.

    *   Final check.
    *   The prompt asks for "Write only the Python code".
    *   I will provide the code block.

    *   Wait, I'll remove the markdown ticks if I want to be extremely literal. But usually, code blocks are expected. I'll use the code block.

    *   Wait, I need to make sure I don't include `if __name__ == "__main__":` block unless it helps testing. The prompt doesn't ask for a script, just implementation. I will omit the main block.

    *   Okay, proceeding.

```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:
        entry = (-priority, self._counter, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._counter += 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 correct.

    *   Wait, is `job_data` typed as `dict` or `Dict`? The prompt uses `dict` in the return type hint `tuple[str, dict]`. I will use `dict`.

    *   One more check: "Higher priority jobs should be processed before lower priority jobs".
    *   If priority 2 is Critical and 0 is Normal.
    *   -2 vs -0.
    *   -2 < 0.
    *   Heap pops smallest.
    *   -2 pops before 0.
    *   So Priority 2 pops before Priority 0.
    *   This means Higher Priority (2) is processed before Lower Priority (0).
    *   Matches requirement.

    *   Okay, done thinking.

</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:
        entry = (-priority, self._counter, job_id, job_data)
        heapq.heappush(self._heap, entry)
        self._counter += 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>