<reasoning_content>
Thinking Process:

1.  **Analyze the Request:**
    *   Goal: Implement priority-based job scheduling for a queue.
    *   Class Name: `JobQueue` (implied by the example usage).
    *   Methods to Implement/Modify:
        *   `add_job(name, metadata, priority=0)`: Add a job with an optional priority.
        *   `get_next_job() -> tuple[str, dict] | None`: Retrieve the highest priority job.
    *   Logic:
        *   Higher priority number -> processed first (2 > 1 > 0).
        *   Same priority -> FIFO (First In, First Out).
    *   Output: Only Python code.

2.  **Data Structure Selection:**
    *   I need a structure that supports:
        *   Insertion with priority.
        *   Retrieval and removal of the highest priority item.
        *   Stability (FIFO for same priority).
    *   *Option A: List.* Insert at correct index. `O(N)` insertion. `O(1)` retrieval.
    *   *Option B: Heap (Priority Queue).* Python's `heapq` is a min-heap. To make it a max-heap (higher priority first), I can negate the priority. To handle FIFO for same priority, I need a tie-breaker (counter).
        *   Tuple structure in heap: `(priority, insertion_order, name, metadata)`.
        *   Since `heapq` pops the smallest, I should store `(-priority, insertion_order, name, metadata)`.
        *   Wait, the requirement says "Higher priority jobs should be processed before lower priority jobs". If priority is 2 (Critical) and 0 (Normal), 2 should come out first.
        *   Python `heapq` returns smallest. So, store `-priority`. If priorities are equal (e.g., -2 and -2), it looks at the next element.
        *   To maintain FIFO, the next element should be the insertion index. Since `heapq` is a min-heap, smaller index comes out first. So just store `( -priority, index, ... )`.
    *   *Option C: Sorted List.* Similar to Option A.
    *   *Option D: `heapq` is standard and efficient ($O(\log N)$).* Let's stick with `heapq`.

3.  **Designing the Heap Item:**
    *   We need to store: `name` (str), `metadata` (dict), `priority` (int).
    *   Heap tuple: `(-priority, counter, name, metadata)`.
    *   `counter` needs to increment for every job added to ensure strict ordering for same priorities.

4.  **Drafting the Code:**

   ```python
   import heapq

   class JobQueue:
       def __init__(self):
           self.heap = []
           self.counter = 0

       def add_job(self, name: str, metadata: dict, priority: int = 0):
           # Higher priority number means higher priority.
           # heapq is a min-heap, so we negate priority to get max-heap behavior.
           # Tuple: (-priority, counter, name, metadata)
           # counter ensures FIFO for same priority.
           heapq.heappush(self.heap, (-priority, self.counter, name, metadata))
           self.counter += 1

       def get_next_job(self) -> tuple[str, dict] | None:
           if not self.heap:
               return None
           
           # Pop the smallest item (which is the highest priority due to negation)
           # Tuple structure: (-priority, counter, name, metadata)
           _, _, name, metadata = heapq.heappop(self.heap)
           return (name, metadata)
   ```

5.  **Refining based on constraints:**
    *   Requirement 1: `add_job` accepts optional priority (default=0). Checked.
    *   Requirement 2: Higher priority processed first. Checked (via negation).
    *   Requirement 3: Same priority FIFO. Checked (via counter).
    *   Requirement 4: `get_next_job` returns `tuple[str, dict] | None`. Checked.
    *   Example match:
        *   add "normal", pri=0 -> heap: `(0, 0, "normal", ...)`
        *   add "critical", pri=2 -> heap: `(-2, 1, "critical", ...)` (Wait, -2 < 0, so critical is "smaller" in min-heap, comes out first).
        *   add "high", pri=1 -> heap: `(-1, 2, "high", ...)`
        *   Pop: `(-2, ...)` comes out first. Correct.

6.  **Edge Cases:**
    *   Empty queue: `get_next_job` returns `None`. Handled.
    *   Metadata modification? The prompt implies returning the job. Usually, in a queue, the item is removed. `heappop` removes it.

7.  **Final Code Structure:**
    *   Imports: `heapq`, `typing` (optional but good for type hinting, though standard syntax `tuple[str, dict]` is valid in Python 3.9+). The prompt uses `tuple[str, dict] | None` syntax, implying Python 3.10+. I will assume modern Python.

8.  **Code Construction:**

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

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

       def add_job(self, name: str, metadata: dict, priority: int = 0) -> None:
           # heapq is a min-heap. To prioritize higher numbers first, we negate the priority.
           # We include a counter to ensure FIFO order for jobs with the same priority.
           # Tuple structure: (-priority, counter, name, metadata)
           heapq.heappush(self._queue, (-priority, self._counter, name, metadata))
           self._counter += 1

       def get_next_job(self) -> Optional[Tuple[str, dict]]:
           if not self._queue:
               return None
           
           # Pop the item with the smallest tuple value (highest priority due to negation)
           # The counter ensures that among equal priorities, the earliest added job is popped first.
           _, _, name, metadata = heapq.heappop(self._queue)
           return (name, metadata)
   ```

   *Self-Correction on Type Hints:* The prompt example uses `tuple[str, dict] | None`. I should stick to standard Python syntax. I don't need explicit imports if I just write the class, but `heapq` is needed. I will include the import.

   Let's check the constraints again.
   "Higher priority jobs should be processed before lower priority jobs"
   Priority 2 (Critical) vs 0 (Normal).
   Heap stores `(-2, ...)` and `(0, ...)`.
   `-2 < 0`. `heappop` returns `-2`. Correct.

   What if priorities are negative? The prompt lists 0, 1, 2. It doesn't explicitly forbid negatives, but usually priority queues handle integers. Negating works for negatives too (e.g., -5 is lower priority than -2? No, usually higher number = higher priority).
   If input is -10 and -5.
   -(-10) = 10.
   -(-5) = 5.
   5 < 10. So -5 comes out before -10. -5 > -10.
   So negation works generally for "higher number = higher priority".

   One detail: The prompt example:
   `queue.add_job("normal", {"type": "normal"}, priority=0)`
   `queue.add_job("critical", {"type": "critical"}, priority=2)`
   
   If I use `heapq`, I need to import it.

   Final check on output format: "Write only the Python code, no explanations."

   ```python
   import heapq

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

       def add_job(self, name: str, metadata: dict, priority: int = 0) -> None:
           # Use negative priority for max-heap behavior with min-heap implementation
           # Counter ensures FIFO order for same priority
           heapq.heappush(self._heap, (-priority, self._counter, name, metadata))
           self._counter += 1

       def get_next_job(self) -> tuple[str, dict] | None:
           if not self._heap:
               return None
           
           _, _, name, metadata = heapq.heappop(self._heap)
           return (name, metadata)
   ```
   This looks solid.

   Wait, is `tuple[str, dict] | None` valid syntax in the environment? It's valid in Python 3.10+. The prompt uses it, so I should use it or `Optional`. I'll use the syntax provided in the prompt for the return type hint to be safe/consistent.

   One minor thing: `metadata` is a dict. If the caller modifies the returned dict, it modifies the internal state. Usually, for a queue, you might want to copy it, but the prompt doesn't specify deep copy. Standard queue behavior usually returns the reference or the object. Given "tuple[str, dict]", returning the reference is the standard interpretation unless specified otherwise.

   Let's double check the prompt's `add_job` signature in the example:
   `queue.add_job("normal", {"type": "normal"}, priority=0)`
   Arguments: `name`, `metadata`, `priority`.
   My signature: `def add_job(self, name: str, metadata: dict, priority: int = 0)`. Matches.

   `get_next_job` signature:
   `def get_next_job(self) -> tuple[str, dict] | None:`
   Matches.

   Implementation details:
   - `heapq` module is standard.
   - `self._counter` handles the tie-breaking.

   Final verification of logic:
   1. `add_job("normal", ..., 0)` -> push `(0, 0, "normal", ...)`
   2. `add_job("critical", ..., 2)` -> push `(-2, 1, "critical", ...)`
   3. `add_job("high", ..., 1)` -> push `(-1, 2, "high", ...)`
   
   Heap contents: `[(-2, 1, "critical", ...), (0, 0, "normal", ...), (-1, 2, "high", ...)]` (simplified view, actual heap structure varies but order is maintained).
   `heappop` returns smallest. `-2` is smallest. Returns `("critical", ...)`. Correct.
   Next `heappop` returns next smallest. `-1` < `0`. Returns `("high", ...)`. Correct.
   Next returns `("normal", ...)`. Correct.

   What if two items have priority 2?
   1. `add_job("A", ..., 2)` -> `(-2, 0, "A", ...)`
   2. `add_job("B", ..., 2)` -> `(-2, 1, "B", ...)`
   Heap: `[(-2, 0, "A", ...), (-2, 1, "B", ...)]`
   Pop: `(-2, 0, ...)` is smaller than `(-2, 1, ...)` because `0 < 1`.
   Returns A. FIFO maintained. Correct.

   The code seems complete.

   One stylistic choice: Should I define the class inside the code block? Yes.
   Should I include imports? Yes, `heapq` is necessary.

   Final check on "Write only the Python code".

   ```python
   import heapq

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

       def add_job(self, name: str, metadata: dict, priority: int = 0) -> None:
           # Store as (-priority, counter, name, metadata)
           # Negative priority allows heapq (min-heap) to act as max-heap
           # Counter ensures FIFO for equal priorities
           heapq.heappush(self._heap, (-priority, self._counter, name, metadata))
           self._counter += 1

       def get_next_job(self) -> tuple[str, dict] | None:
           if not self._heap:
               return None
           
           _, _, name, metadata = heapq.heappop(self._heap)
           return (name, metadata)
   ```
   This looks perfect.

   Wait, the prompt asks for `get_next_job() -> tuple[str, dict] | None`.
   In older Python versions `|` for union types isn't supported without `from __future__ import annotations` or using `typing.Union`. However, since the prompt *uses* that syntax in the requirement description, I will use it. If strict compatibility is needed, `Optional[Tuple[str, dict]]` is safer, but usually, prompts like this imply modern Python 3.10+ features are acceptable or desired. I'll stick to the prompt's syntax style.

   Actually, to be safest and most robust, I will use `Optional` from `typing` or just the syntax provided. I'll stick to the syntax provided in the prompt for the return type.

   One detail: The prompt example uses `queue = JobQueue()`. It doesn't show imports. I must include `import heapq`.

   Final check of the code block.

```python
import heapq

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

    def add_job(self, name: str, metadata: dict, priority: int = 0) -> None:
        # heapq is a min-heap. We negate priority to process higher numbers first.
        # We use a counter to maintain FIFO order for jobs with the same priority.
        heapq.heappush(self._heap, (-priority, self._counter, name, metadata))
        self._counter += 1

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._heap:
            return None
        # Pop the item with the smallest tuple (highest priority due to negation)
        _, _, name, metadata = heapq.heappop(self._heap)
        return (name, metadata)
```
   Looks good.

   Wait, is `metadata` mutable? Yes. If I return the reference, the user can modify it. The prompt doesn't ask for a copy.
   
   One tiny check: `tuple[str, dict] | None` syntax is Python 3.10+. If the environment is 3.9, this fails. But the prompt used it. I will use it.

   Wait, looking at the prompt again:
   `get_next_job() -> tuple[str, dict] | None`
   This is definitely Python 3.10+ syntax.

   I will provide the code now.

   Double check: "Modify `add_job`". The prompt implies `add_job` might already exist or I need to create the class. "Implement priority-based job scheduling... Modify `add_job`...". Since I don't have the base code, I must provide the full class implementation.

   Resulting Code:
```python
import heapq

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

    def add_job(self, name: str, metadata: dict, priority: int = 0) -> None:
        # Use negative priority because heapq is a min-heap
        # Counter ensures FIFO order for same priority
        heapq.heappush(self._heap, (-priority, self._counter, name, metadata))
        self._counter += 1

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._heap:
            return None
        
        _, _, name, metadata = heapq.heappop(self._heap)
        return (name, metadata)
```
   This satisfies all constraints.

   One last thought: Does `add_job` need to return anything? The prompt doesn't specify a return type for `add_job`, so `None` (implicit) is fine.

   The prompt asks for "Write only the Python code". I will output the block.

   Wait, if I want to be super safe about the `tuple[str, dict] | None` syntax, I could check if I should import `Optional`. But usually, prompts using that syntax expect it to work. I'll stick to it.

   Actually, looking at the provided example:
   `job = queue.get_next_job()  # Returns critical job first`
   The variable `job` would be a tuple.

   Okay, ready.

   One minor optimization/alternative:
   If I don't want to negate priority (in case priority is float or something weird, though int is specified), I could use a custom class with `__lt__`. But tuple comparison is faster and standard for heaps. Negation is the standard Pythonic way for max-heap.

   Final check on "Higher priority jobs should be processed before lower priority jobs".
   Priority 2 (Critical) vs 0 (Normal).
   Code: `(-2, ...)` vs `(0, ...)`.
   `-2 < 0`.
   `heappop` returns `-2`.
   Correct.

   What about `priority=1` (High)?
   Code: `(-1, ...)`.
   Order of popping: `-2` (2), `-1` (1), `0` (0).
   Correct.

   What about negative priorities?
   If priority is -1.
   Code: `1`.
   If priority is -2.
   Code: `2`.
   -1 > -2.
   1 < 2.
   So -1 comes out before -2.
   Correct.

   The logic holds.

   One detail: The prompt says "Modify `add_job`". It might imply there is an existing class structure I don't see, but usually in these LLM prompts, it means "Write the code for the class including this method". I will provide the whole class.

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

   One edge case: `metadata` is a dict.
   In `add_job`, `metadata` is passed.
   In `get_next_job`, it returns the dict.
   If the user modifies the dict returned, does it affect the queue?
   Since `heappop` removes the item from the heap, the reference is gone from the heap. So modifying the returned dict doesn't affect the queue (unless the same dict object was added twice, but that's a user error).
   However, if I didn't pop, it would matter. Since I pop, it's fine.

   Wait, `heapq.heappop` removes the item. So the reference `metadata` in the tuple is removed from the list. The user gets a reference to the dict. Modifying it is fine, it won't be in the queue anymore.

   Okay, good to go.

   Wait, I should check if `typing` imports are needed for `tuple[str, dict]`.
   In Python 3.9+, `list[str]` etc work without import. `tuple` works too.
   The `|` union syntax requires 3.10.
   I will assume 3.10+.

   If I want to be 100% compatible with 3.8+ (just in case), I would use `Optional[Tuple[str, Dict]]`.
   But the prompt wrote `tuple[str, dict] | None`. I will follow the prompt's lead.

   Final Code:
```python
import heapq

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

    def add_job(self, name: str, metadata: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, name, metadata))
        self._counter += 1

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._heap:
            return None
        _, _, name, metadata = heapq.heappop(self._heap)
        return (name, metadata)
```
   I'll format it nicely.

   Wait, I missed one thing. `metadata` in `add_job` is `dict`. In `get_next_job` return type it is `dict`.
   In Python 3.9+ `dict` is subscriptable? No, `dict` is a type, but `dict[str, str]` works.
   The prompt uses `dict` (unsubscripted) in `add_job` argument `metadata: dict`.
   And `tuple[str, dict]` in return.
   This is consistent.

   I will write the code now.

   One final check on constraints:
   "Higher priority jobs should be processed before lower priority jobs"
   "Jobs with the same priority should maintain FIFO order"
   "add_job ... optional priority parameter (default=0)"
   "get_next_job() -> tuple[str, dict] | None"

   All covered.

   One thought: Should I copy the metadata dict?
   `queue.add_job("job1", {"data": 123})`
   `job = queue.get_next_job()`
   `job[1]["data"] = 456`
   If I stored the reference, and the user modifies it, it's fine because the job is removed.
   But what if `get_next_job` didn't remove it? (Not the case here).
   What if I pushed the same dict object twice?
   `d = {"a": 1}`
   `q.add_job("j1", d)`
   `q.add_job("j2", d)`
   `q.get_next_job()` -> returns ("j1" or "j2", d)
   If I return `d`, and user modifies it, the other job's metadata is also modified.
   This is a side effect.
   However, usually in these coding tasks, shallow copy is not required unless specified ("immutable" or "copy"). I will stick to returning the reference to keep it simple and efficient, as is standard for `heappop`.

   Actually, to be safe against the "same object added twice" case (though unlikely in simple test cases), returning the reference is the standard behavior of a pop operation.

   I'll stick to the reference.

   Wait, if I use `heapq`, the tuple is `(-priority, counter, name, metadata)`.
   If I return `(name, metadata)`, I am returning a new tuple, but the `metadata` inside is the same object reference.
   This is acceptable.

   Final check: "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, name: str, metadata: dict, priority: int = 0) -> None:
        # Use negative priority to simulate max-heap behavior with min-heap
        # Use counter to ensure FIFO order for equal priorities
        heapq.heappush(self._heap, (-priority, self._counter, name, metadata))
        self._counter += 1

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._heap:
            return None
        
        # Pop the smallest item (highest priority)
        _, _, name, metadata = heapq.heappop(self._heap)
        return (name, metadata)
```
   The comments are helpful but the prompt said "no explanations". Comments in code are usually fine, but I'll keep them minimal or remove them to be strictly "code only". I'll keep them for clarity as they explain the logic, which is often preferred even in "code only" requests, but I'll strip verbose explanations.

   Actually, "Write only the Python code, no explanations" usually means "Don't write text outside the code block explaining what you did". Comments inside are part of the code.

   I'll provide clean code.

```python
import heapq

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

    def add_job(self, name: str, metadata: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, name, metadata))
        self._counter += 1

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._heap:
            return None
        _, _, name, metadata = heapq.heappop(self._heap)
        return (name, metadata)
```
   This is concise and correct.

   One detail: The prompt uses `tuple[str, dict] | None`.
   If I run this on Python 3.9, it throws `TypeError: unsupported operand type(s) for |: 'types.GenericAlias' and 'NoneType'`.
   However, the prompt *explicitly* used that syntax in the requirements.
   `Add get_next_job() -> tuple[str, dict] | None`
   So I must use that syntax.

   Final check: `add_job` default priority 0. `priority: int = 0`. Correct.

   I am ready.

   Wait, I should check if `metadata` needs to be a copy.
   If I do:
   `q.add_job("a", {"x": 1})`
   `job = q.get_next_job()`
   `job[1]["x"] = 2`
   The dict is modified.
   If the queue implementation stored the reference, and I hadn't popped, it would be an issue.
   Since I popped, the queue no longer holds the reference. The user holds the reference.
   It's fine.

   Wait, if I push multiple times with the *same* dict object?
   `d = {"x": 1}`
   `q.add_job("a", d)`
   `q.add_job("b", d)`
   `q.get_next_job()` -> returns ("a", d) (assuming priority order)
   User modifies `d`.
   Now the job "b" (still in queue) has metadata `{"x": 2}`.
   This is a side effect.
   To prevent this, I should copy the metadata.
   `metadata=metadata.copy()` or `metadata=dict(metadata)`.
   However, copying dicts can be expensive if metadata is large.
   Standard queue implementations usually just store references unless immutability is required.
   Given the simplicity of the prompt, I will assume shallow copy is not strictly required, but it's a "best practice".
   However, `metadata: dict` suggests it's a mutable object.
   I'll stick to reference to avoid overhead unless specified, but `metadata.copy()` is safer.
   Let's look at the example: `{"type": "normal"}`. Small dict.
   I'll add `.copy()` just to be robust?
   Actually, if I look at standard library `queue.Queue`, it stores references.
   I will store references.

   Wait, the prompt asks to "Implement priority-based job scheduling".
   Usually, jobs are immutable data or the queue owns the data until popped.
   I'll stick to reference. It's the most direct interpretation.

   Final code.

```python
import heapq

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

    def add_job(self, name: str, metadata: dict, priority: int = 0) -> None:
        heapq.heappush(self._heap, (-priority, self._counter, name, metadata))
        self._counter += 1

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._heap:
            return None
        _, _, name, metadata = heapq.heappop(self._heap)
        return (name, metadata)
```
   This is the safest bet.

   Wait, I just realized `tuple[str, dict] | None` is the return type.
   In the code `return (name, metadata)`, `name` is str, `metadata` is dict.
   Tuple type is `tuple[str, dict]`.
   Matches.

   One last check: `add_job` arguments order.
   Example: `queue.add_job("normal", {"type": "normal"}, priority=0)`
   My code: `def add_job(self, name: str, metadata: dict, priority: int = 0)`
   Matches.

   I'm confident.

   Wait, I should check if `heapq` is available. Yes, standard library.

   Final output generation.

   Wait, I see "Write only the Python code".
   I will wrap it in a code block.

   One thought: The prompt example `queue = JobQueue()`.
   Does `JobQueue` need to be defined? Yes.
   Does it need `__init__`? Yes.

   Okay.

   Wait, if I want to be extremely precise about "Higher priority jobs should be processed before lower priority jobs".
   If priority is 2 and 1.
   2 > 1.
   Heap stores -2 and -1.
   -2 < -1.
   -2 pops first.
   So 2 pops first.
   Correct.

   If priority is 1 and 2.
   2 > 1.
   2 pops first.
   Correct.

   If priority is 0 and 0.
   Counter 0 and 1.
   0 pops first.
   Correct.

   Logic is sound.

   I'll format the code now.

</reasoning_content>

<content>
```python
import heapq

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

    def add_job(self, name: str, metadata: dict, priority: int = 0) -> None:
        # heapq is a min-heap, so we negate priority to process higher numbers first.
        # We use a counter to ensure FIFO order for jobs with the same priority.
        heapq.heappush(self._heap, (-priority, self._counter, name, metadata))
        self._counter += 1

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._heap:
            return None
        
        # Pop the item with the smallest tuple value (highest priority due to negation)
        _, _, name, metadata = heapq.heappop(self._heap)
        return (name, metadata)
```
</content>