<reasoning_content>
Thinking Process:

1.  **Analyze the Request:**
    *   Goal: Implement a priority-based job scheduling queue in Python.
    *   Specific Requirements:
        1.  `add_job` method needs an optional `priority` parameter (default=0).
        2.  Higher priority numbers mean higher priority (e.g., 2 > 1 > 0).
        3.  Same priority jobs must maintain FIFO order.
        4.  Add `get_next_job()` method returning `tuple[str, dict] | None`.
    *   Constraint: Output *only* the Python code, no explanations.
    *   Example provided shows priorities: Critical (2) > High (1) > Normal (0).

2.  **Determine Data Structure:**
    *   To maintain priority ordering and FIFO within the same priority, a priority queue (heap) is suitable.
    *   However, Python's `heapq` is a min-heap. Since higher priority numbers should be processed first, I need to negate the priority or use a max-heap approach.
    *   Alternatively, I can store tuples `(priority, timestamp, job_name, job_data)` in a heap.
    *   To ensure FIFO for same priorities, I need a counter or timestamp to break ties. Since it's a min-heap, if I use `(priority, counter, ...)` where priority is inverted (e.g., `-priority`), the smallest `-priority` (highest actual priority) comes first. Wait, the requirement says "Higher priority jobs should be processed before lower priority jobs".
    *   Let's check the example:
        *   Normal: 0
        *   High: 1
        *   Critical: 2
    *   So 2 is higher than 0.
    *   Python `heapq` pops the smallest item.
    *   To make 2 pop before 0, I can store `(-priority, counter, ...)` so that -2 < 0.
    *   To ensure FIFO for same priority, I need a monotonic counter. Since we want FIFO for same priority, the counter should increment.
    *   Tuple structure for heap: `(-priority, insertion_order, job_name, job_data)`.
    *   `heapq.heappush` will order by -priority (descending actual priority), then by insertion_order (FIFO).

3.  **Drafting the Class:**
    *   Class name: `JobQueue` (inferred from example).
    *   Imports: `heapq`, `typing` (for type hints).
    *   Attributes: `heap`, `counter` (for FIFO tie-breaking).

4.  **Implementing `add_job`:**
    *   Signature: `def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:`
    *   Logic: Increment counter, push to heap.

5.  **Implementing `get_next_job`:**
    *   Signature: `def get_next_job(self) -> tuple[str, dict] | None:`
    *   Logic: Pop from heap. If heap empty, return None. Unpack data. Return `(job_name, job_data)`.

6.  **Refining Type Hints:**
    *   The prompt uses `tuple[str, dict] | None`. I should match this style.
    *   Need to import `heapq`.

7.  **Addressing Priority Logic:**
    *   Requirement: "Higher priority jobs should be processed before lower priority jobs".
    *   Example: 2 (Critical) > 1 (High) > 0 (Normal).
    *   Heap is a min-heap.
    *   If I store `priority`, 0 < 1 < 2. 0 comes first. That's wrong.
    *   If I store `-priority`, -2 < -1 < 0. -2 comes first. That works.
    *   Tie-breaking: FIFO.
    *   If priority is same, e.g., 0 and 0.
    *   Heap item: `(-priority, counter, name, data)`.
    *   If priorities are equal, counter decides. Counter increments. Smallest counter comes first. That works for FIFO.

8.  **Final Code Structure:**
    *   Imports.
    *   Class definition.
    *   `__init__`.
    *   `add_job`.
    *   `get_next_job`.

9.  **Verification:**
    *   `add_job("normal", ..., priority=0)` -> Heap: `(0, 0, ...)`
    *   `add_job("critical", ..., priority=2)` -> Heap: `(-2, 1, ...)`
    *   `add_job("high", ..., priority=1)` -> Heap: `(-1, 2, ...)`
    *   Pop:
        *   `(-2, 1, ...)` vs `(-1, 2, ...)` vs `(0, 0, ...)`.
        *   Smallest is `-2` (Critical). Correct.
    *   Next:
        *   `(-1, 2, ...)` vs `(0, 0, ...)`.
        *   Smallest is `-1` (High). Correct.
    *   Next:
        *   `(0, 0, ...)`. Correct.
    *   Same priority:
        *   Job A (priority 0, counter 0).
        *   Job B (priority 0, counter 1).
        *   Heap: `[(0, 0, A), (0, 1, B)]`.
        *   Pop A. Pop B. FIFO maintained. Correct.

10. **Writing the code:**
    *   Ensure no markdown text outside the code block (though the prompt says "Write only the Python code", usually implies a markdown code block is fine, but strictly "no explanations" means no text before/after). I will provide the code inside a markdown block for clarity as is standard for code output, but strictly following "only Python code". I will output the raw text of the code. Wait, usually "Write only the Python code" implies I shouldn't write `Here is the code:`. I will wrap it in triple backticks for clarity as per standard LLM behavior, but keep the content clean.
    *   Actually, looking at strict constraints: "Write only the Python code". I should avoid markdown ticks if possible, but usually, a code block is expected. I'll use a code block.
    *   Wait, I need to check if imports are allowed. Yes, standard library.
    *   Type hints: `tuple[str, dict] | None` requires Python 3.10+. I will use that syntax.
    *   Class name: `JobQueue`.

    Let's refine the implementation details.
    ```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:
            # Negate priority because heapq is a min-heap
            # Higher priority (e.g., 2) should come before lower (e.g., 0)
            # -2 < -1 < 0, so -2 pops first.
            item = (-priority, self._counter, job_name, job_data)
            self._counter += 1
            heapq.heappush(self._heap, item)

        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, `Tuple[str, dict] | None` is valid in Python 3.10+.
    The prompt example uses `tuple[str, dict] | None`. I will stick to lowercase `tuple` as per the prompt's type hint style.
    Also, `job_data` in the example is `{"type": "normal"}`.
    The prompt asks for `get_next_job() -> tuple[str, dict] | None`.
    I will use `tuple` instead of `Tuple` for consistency with the prompt's type hint style.

    One detail: `add_job` signature in prompt example: `queue.add_job("normal", {"type": "normal"}, priority=0)`.
    So `job_name` is the first arg, `job_data` is the second.
    My draft matches this.

    Double check: "Write only the Python code". I will output the code block.

    Wait, one edge case: `job_data` type hint in `add_job`. The prompt example passes a dict.
    I should ensure imports are included if necessary. `heapq` is needed.

    Final check on constraints:
    1. `add_job` optional priority (default 0). Checked.
    2. Higher priority first. Checked (using negation).
    3. Same priority FIFO. Checked (using counter).
    4. `get_next_job` return type. Checked.

    Code looks good.

    One minor thing: The prompt example shows `queue = JobQueue()`. So the class name must be `JobQueue`.

    Let's write it.

```python
import heapq
from typing import 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:
        # Negate priority so higher numbers are popped first (min-heap behavior)
        # Counter ensures FIFO order for same priority
        heapq.heappush(self._heap, (-priority, self._counter, job_name, job_data))
        self._counter += 1

    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 `typing` is needed for the return type `tuple[str, dict] | None`. In Python 3.10+, `tuple` is a generic alias that works without imports. However, `heapq` is needed. I will include `import heapq`. I don't need `typing` explicitly if I use built-in generics for `tuple`.

    Wait, `tuple[str, dict]` works in 3.9+ with `from __future__ import annotations` or just natively in 3.10+. Since I don't know the environment, standard practice is to just write the code.
    I will include `import heapq`.

    One more check: "Write only the Python code". I should not include markdown ticks if I want to be extremely strict, but usually, a code block is interpreted as code. I will provide the code inside a markdown block.

    Wait, I should check if `job_data` should be typed as `dict` or `Dict[str, any]`. The prompt example uses `dict`. I'll use `dict`.

    Final check on priority logic.
    Critical (2) vs Normal (0).
    Heap push: `(-2, ...)` vs `(0, ...)`.
    Min-heap pops smallest.
    `-2` is smaller than `0`.
    So Critical pops before Normal. Correct.

    FIFO check.
    Job A (priority 0, counter 0).
    Job B (priority 0, counter 1).
    Heap: `[(0, 0, A), (0, 1, B)]`.
    Pop: `(0, 0, A)` is smaller than `(0, 1, B)` because 0 < 1.
    So A pops before B. Correct.

    Ready to generate.

    Wait, I need to make sure I don't output any markdown explanation text like "Here is the code". Just the code.

    One detail: The prompt asks for `tuple[str, dict] | None`.
    I will use that exact syntax.

    One more detail: `job_data` in `add_job` is a dict.
    In `get_next_job`, it returns the data dict.

    Okay, writing the code now.

```python
import heapq
from typing import 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:
        heapq.heappush(self._heap, (-priority, self._counter, job_name, job_data))
        self._counter += 1

    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, `typing` import isn't strictly needed for `tuple` if using 3.10+ syntax, but `heapq` is.
    I'll remove `from typing import Optional` since it's not used in the type hints provided in the prompt (prompt uses `tuple` not `Optional`).
    Actually, `tuple[str, dict] | None` is valid in 3.10+.
    I will just import `heapq`.

    Wait, I should check if the user expects the class to be named `JobQueue`. The example uses `queue = JobQueue()`. So yes.

    Final check on imports. `heapq` is standard.
    The prompt says "Write only the Python code".
    I will output the code block.

    One nuance: `job_data` type hint in `add_job`. The example passes `{"type": "normal"}`.
    So `job_data: dict` is fine.

    Let's assemble.

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

    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>