<reasoning_content>
Thinking Process:

1.  **Analyze the Request:**
    *   Goal: Implement priority-based job scheduling for a queue.
    *   Constraints:
        1.  Modify `add_job` to accept an optional `priority` parameter (default=0).
        2.  Higher priority values should be processed first (Priority 2 > Priority 1 > Priority 0).
        3.  Jobs with the same priority must maintain FIFO (First-In-First-Out) order.
        4.  Add `get_next_job() -> tuple[str, dict] | None` method to retrieve the next job.
    *   Output: Only Python code, no explanations.

2.  **Determine Data Structure:**
    *   Need a structure that supports efficient insertion with priority and retrieval of the highest priority item.
    *   `heapq` (priority queue) is a natural choice for priority-based scheduling.
    *   To maintain FIFO order for same priorities, I need a tie-breaker. Since `heapq` compares tuples element by element, I can store tuples like `(priority, insertion_counter, job_name, job_data)`.
    *   Wait, the requirement says "Higher priority jobs should be processed before lower priority jobs". Usually, priority queues pop the *smallest* item. If priority 2 is Critical and 0 is Normal, 2 > 0. So if I use a standard min-heap, I should store `(-priority, insertion_counter, ...)` so that 2 becomes -2 (smaller) and pops before -1.
    *   Alternatively, I can use a max-heap logic by negating priority.
    *   Let's check the example:
        *   `priority=0` (Normal)
        *   `priority=2` (Critical)
        *   `priority=1` (High)
        *   Output: Critical (2) first.
        *   So, 2 > 1 > 0.
        *   If using `heapq` (min-heap), I need to store `-priority` so that -2 < -1 < 0. Then -2 pops first.
    *   Tie-breaking: If priorities are equal, insertion order matters (FIFO). `heapq` compares the second element of the tuple if the first is equal. I need a global counter to ensure strict ordering for FIFO within the same priority.

3.  **Drafting the Class:**
    *   Class name: `JobQueue` (implied by the example usage `queue = JobQueue()`).
    *   Instance variables:
        *   `self.heap`: list for `heapq`.
        *   `self.counter`: integer to track insertion order for FIFO stability.
    *   Method `add_job(job_name: str, job_data: dict, priority: int = 0)`:
        *   Increment counter.
        *   Push `(-priority, counter, job_name, job_data)` to heap. (Negating priority because `heapq` is a min-heap).
    *   Method `get_next_job() -> tuple[str, dict] | None`:
        *   Pop from heap.
        *   Return `(job_name, job_data)`.
        *   Handle empty queue case.

4.  **Refining the Tie-Breaker:**
    *   If I use `(-priority, counter, ...)`, then for same priority, smaller `counter` comes first. Since `counter` increments, earlier jobs have smaller counters. This satisfies FIFO for same priority.
    *   Wait, is `counter` strictly necessary if I just append to a list and sort? No, `heapq` is O(log N) push/pop. Sorting is O(N). Heap is better.
    *   Actually, Python's `heapq` compares tuples. If `(-priority, counter)` are compared:
        *   Job A: (-2, 1)
        *   Job B: (-2, 2)
        *   A < B, so A pops first. Correct.

5.  **Refining the Return Type:**
    *   `get_next_job` should return `tuple[str, dict] | None`.
    *   Input signature for `add_job`: `def add_job(self, job_name: str, job_data: dict, priority: int = 0) -> None:`
    *   Input signature for `get_next_job`: `def get_next_job(self) -> tuple[str, dict] | None:`

6.  **Writing the Code:**
    *   Import `heapq`.
    *   Define class `JobQueue`.
    *   Implement `__init__`.
    *   Implement `add_job`.
    *   Implement `get_next_job`.
    *   Ensure imports are present.

7.  **Self-Correction/Edge Case:**
    *   Does `job_data` need to be a copy? Usually good practice, but requirement doesn't specify. I'll pass it as is.
    *   Does `priority` need validation? Requirement doesn't specify. I'll assume valid int.
    *   The example shows `queue.add_job("normal", {"type": "normal"}, priority=0)`.
    *   Wait, looking at the example call: `queue.add_job("normal", {"type": "normal"}, priority=0)`.
    *   The arguments are `job_name`, `job_data`, `priority`.
    *   Wait, standard `heapq` behavior:
        *   `heapq.heappush(heap, item)`
        *   `item` is a tuple.
        *   Comparison is lexicographical.
    *   So `(-priority, counter, name, data)` works.

8.  **Final 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:
            heapq.heappush(self.heap, (-priority, self.counter, job_name, job_data))
            self.counter += 1

        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 requirement says `get_next_job() -> tuple[str, dict] | None`.
    *   In Python 3.9+, `|` is supported for type hinting. In older versions, `Union` is needed. I'll stick to standard typing or just the syntax provided in the prompt (`tuple[str, dict] | None`).
    *   The prompt example usage: `queue.add_job("normal", {"type": "normal"}, priority=0)`.
    *   The prompt asks for "Write only the Python code".

    *   Wait, one detail in the prompt example: `queue.add_job("normal", {"type": "normal"}, priority=0)`.
    *   It implies `job_name` is the first arg, `job_data` is the second.
    *   Let's check the priority logic again.
    *   "Higher priority jobs should be processed before lower priority jobs".
    *   Example: Priority 2 (Critical) > Priority 0 (Normal).
    *   Min-heap pops smallest.
    *   If I store `priority`, 0 < 2. 0 pops before 2. That's wrong.
    *   If I store `-priority`, -2 < -0. -2 pops before 0. That is correct.
    *   So `(-priority, ...)` is correct.

    *   Wait, is `priority` an integer? Yes.
    *   Is the counter needed? Yes, to maintain FIFO for same priority. If I don't use counter, heap stability isn't guaranteed by Python's `heapq` (it's not stable sort inherently, though tuple comparison handles equality). Actually, if priorities are equal, the heap might compare the next element. If I don't have a counter, `( -1, "a", ...)` and `(-1, "b", ...)` compare strings. That breaks FIFO based on insertion time. So counter is necessary.

    *   Wait, I should check if the prompt implies a specific class name. It uses `queue = JobQueue()`. So class name `JobQueue` is expected.

    *   One more check: `get_next_job() -> tuple[str, dict] | None`.
    *   I should make sure the imports are included so the code runs.

    *   Wait, the prompt asks for "Implement priority-based job scheduling for the queue." and gives specific method signatures implicitly via the example. I should define the class matching that usage.

    *   Code check:
    ```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:
            # Use -priority so higher priority pops first (min-heap)
            # Use counter to maintain FIFO 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, `Tuple[str, dict] | None` syntax requires Python 3.10+ or `from __future__ import annotations` or use `Union`. I will use `Tuple[str, dict] | None` as per the prompt's snippet `get_next_job() -> tuple[str, dict] | None`. Note the prompt uses `tuple` (lowercase) which is valid in runtime but `Tuple` is typing. I will use `tuple` and `|` to match the prompt exactly.
    *   Actually, to be safe with standard Python typing, `tuple` is generic in 3.9+ (PEP 585).
    *   I will use `tuple` and `|` as requested in the prompt signature.

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

    *   Wait, one detail: `queue.add_job("normal", {"type": "normal"}, priority=0)`.
    *   The `priority` is a keyword argument.
    *   My implementation `def add_job(self, job_name: str, job_data: dict, priority: int = 0):` matches.

    *   Final check on constraints:
    *   1. `add_job` accepts `priority` (default 0). Checked.
    *   2. Higher priority processed first. Checked (via negation).
    *   3. Same priority FIFO. Checked (via counter).
    *   4. `get_next_job` returns tuple or None. Checked.

    *   Wait, `heapq` compares tuples.
    *   Item 1: `(-2, 0, "critical", ...)`
    *   Item 2: `(-1, 1, "high", ...)`
    *   Item 3: `(0, 2, "normal", ...)`
    *   Comparison:
        *   -2 vs -1: -2 is smaller. Item 1 pops first. Correct.
        *   Same priority: `(-2, 0, ...)` vs `(-2, 1, ...)`.
        *   -2 == -2.
        *   0 < 1. First one pops first. Correct.

    *   Looks good.

    *   One minor thing: `job_data` type hint in `add_job`. The prompt example uses `{"type": "normal"}`. So `dict` is correct.
    *   I will omit type hints for arguments if not strictly necessary to keep it clean, but the prompt asked for `get_next_job() -> tuple[str, dict] | None`, implying type hints are desired/expected. I will include type hints for consistency.

    *   Wait, the prompt says "Write only the Python code". I should include imports.

    *   Wait, `tuple[str, dict]` vs `Tuple[str, dict]`. The prompt uses `tuple`. I will use `tuple`.

    *   Wait, `get_next_job` return type in prompt: `tuple[str, dict] | None`.
    *   I will use that exact signature.

    *   One detail: The prompt example `queue.add_job("normal", {"type": "normal"}, priority=0)`.
    *   This implies `job_name` is string, `job_data` is dict.
    *   I will ensure the method signature matches.

    *   Wait, should I initialize the class with `__init__`? Yes.
    *   Should I import `heapq`? Yes.

    *   Final check on imports. `import heapq`.
    *   Code structure:
    ```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)
    ```
    *   Is `job_data` passed by reference? Yes. That's fine.
    *   Does it need to be deep copied? Usually for queues, you might want to avoid mutation issues, but requirements don't specify. I'll leave it as is.

    *   Wait, one nuance: `priority` default value. `default=0` in requirement 1.
    *   My signature: `priority: int = 0`. Matches.

    *   Wait, requirement 4: `get_next_job() -> tuple[str, dict] | None`.
    *   My signature matches.

    *   Wait, I should check if `tuple` is available without import in Python 3.9+. Yes, `from __future__ import annotations` helps compatibility, but standard `import` works. `tuple` is a builtin.
    *   I'll stick to standard Python 3 syntax.

    *   Wait, I need to make sure I don't output markdown code blocks if the instruction says "Write only the Python code". Usually, markdown blocks are acceptable as code representation, but "only the Python code" might mean raw text. However, standard practice for LLM code output is markdown block. I will use a markdown block to be safe and clear, or just the text. The prompt says "Write only the Python code, no explanations." Usually, this means don't write "Here is the code:" before it. I will wrap it in markdown for clarity as is standard for code output.
    *   Actually, strictly "only the Python code" might mean no markdown ticks. But usually, it means no conversational text. I will use markdown ticks for the code block as it is the standard way to present code. Wait, if I put ```python ... ``` is that "Python code"? Yes.
    *   Let's look at similar requests. Usually, wrapping in ```python is expected.
    *   I will provide the code inside a python block.

    *   Wait, one last check on the `add_job` arguments. The example is `add_job("normal", {"type": "normal"}, priority=0)`.
    *   This implies `job_name` is positional, `job_data` is positional, `priority` is keyword.
    *   My signature: `def add_job(self, job_name: str, job_data: dict, priority: int = 0):`.
    *   This allows `queue.add_job("normal", {"type": "normal"}, priority=0)`.
    *   It also allows `queue.add_job("normal", {"type": "normal"}, 0)`.
    *   This matches.

    *   Wait, `job_data` type hint. `dict` is generic. `Dict` from typing is better for older Python. But `dict` works for 3.9+. I will use `dict` as per prompt hint `tuple[str, dict]`.

    *   Ready to write.

    *   Wait, I need to ensure `heapq` is imported.

    *   One detail: `get_next_job` returns `tuple[str, dict]`.
    *   In the heap, I store `( -priority, counter, job_name, job_data )`.
    *   When popping, I extract `job_name` and `job_data`.
    *   Return `(job_name, job_data)`.
    *   Matches.

    *   Wait, if I use `dict` in type hint, it might require `from typing import Dict` in older versions. In 3.9+ `dict` works. I will assume modern Python.
    *   I will use `dict` and `tuple` as per the prompt's `tuple[str, dict]`.

    *   Wait, I should check if `priority` can be negative. The example uses 0, 1, 2. Higher is better.
    *   My logic `-priority` handles positive integers correctly (2 becomes -2, 0 becomes 0). -2 < 0. Correct.
    *   If priority was 3, -3 < -2. Correct.
    *   If priority was -1, -(-1) = 1. 1 > 0. So -1 would be lower priority than 0.
    *   Requirement says: 0 Normal, 1 High, 2 Critical.
    *   So 2 > 1 > 0.
    *   My logic holds.

    *   Final check on "Write only the Python code". I will output the code block.

```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:
        # Store (-priority, counter, job_name, job_data)
        # Negating priority ensures higher priority pops first (min-heap)
        # 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 not include comments if I want to be strict "only code". But comments are part of code. I will keep them minimal or remove them to be safe. The prompt says "no explanations", comments explaining logic might be borderline. I'll remove comments to be safe.
    *   Wait, the prompt example uses `queue = JobQueue()`. So the class name must be `JobQueue`.
    *   I will remove comments.

    *   Wait, I need to make sure I don't miss `from typing import ...` if I use type hints that require it. `tuple` and `dict` are built-ins in 3.9+. `|` union syntax is 3.10+. If the environment is older, this might fail. But I must follow the prompt's signature style `tuple[str, dict] | None`. I will assume Python 3.10+ compatibility for the syntax provided in the prompt.

    *   Wait, I'll remove type hints from `add_job` arguments if I want to be purely safe, but the prompt signature implies type hints. I'll keep them consistent with the prompt's `get_next_job`.
    *   Actually, I'll keep the signatures clean.

    *   One check: `job_data` in `add_job`. The prompt example passes `{"type": "normal"}`.
    *   So `job_data: dict` is fine.

    *   Final check on imports. `import heapq`.

    *   Okay, generating 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:
        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)
```
    *   Looks correct.

    *   Wait, one detail: `get_next_job` return type in prompt is `tuple[str, dict] | None`.
    *   My code uses `tuple[str, dict] | None`.
    *   Matches.

    *   Wait, I should check if `job_data` should be a copy. If the user modifies `job_data` after adding, it affects the queue. Usually, queues should own the data. But without explicit requirement, I'll pass reference. It's standard for this level of abstraction.

    *   Wait, I will remove the type hints from `add_job` arguments just to be concise, but the prompt signature for `get_next_job` has them. I'll keep `add_job` signatures consistent.

    *   Wait, `tuple` vs `Tuple`. The prompt uses `tuple`. I will use `tuple`.

    *   Okay, ready.

    *   Wait, one more check. `get_next_job` returns `tuple[str, dict]`.
    *   If `heapq` returns `None` (empty), I return `None`.
    *   If not empty, I pop and return tuple.
    *   Correct.

    *   Wait, I need to make sure I don't print anything.
    *   Okay.

    *   Wait, I will double check the priority logic.
    *   Requirement: "Higher priority jobs should be processed before lower priority jobs".
    *   Example: Priority 2 (Critical) before Priority 0 (Normal).
    *   Heapq is min-heap.
    *   Item: `(-priority, counter, ...)`
    *   Job A: priority 2 -> -2.
    *   Job B: priority 0 -> 0.
    *   -2 < 0.
    *   A pops before B.
    *   Correct.

    *   FIFO for same priority.
    *   Job A: priority 1, counter 0.
    *   Job B: priority 1, counter 1.
    *   Item A: (-1, 0, ...)
    *   Item B: (-1, 1, ...)
    *   (-1, 0) < (-1, 1).
    *   A pops before B.
    *   Correct.

    *   Okay, code is 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:
        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 I need to import `Tuple` from `typing` if I use `tuple`. `tuple` is built-in generic in 3.9+. I will assume 3.9+ based on `|` union syntax.
    *   Okay.

    *   Wait, I'll remove the type hints from `add_job` to minimize potential version issues if the environment is strict, but the prompt implies them. I'll keep them to match the style of `get_next_job`.
    *   Actually, `job_data: dict` is fine.

    *   Final check.
    *   Code only.
    *   No explanations.
    *   Python code.

    *   Okay.

</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>