<reasoning_content>
*   Task: Implement priority-based job scheduling for a `JobQueue` class.
    *    Requirements:
        1.  `add_job(job_name, job_data, priority=0)`: Add a job with an optional priority.
        2.  Priority logic: Higher priority (higher number) first.
        3.  Tie-breaking: FIFO (First-In, First-Out) for the same priority.
        4.  `get_next_job() -> tuple[str, dict] | None`: Get and remove the highest priority job.
    *    Priority levels: 0 (Normal), 1 (High), 2 (Critical).

    *   A priority queue is best implemented using a heap (`heapq` module in Python).
    *   `heapq` is a min-heap (smallest value at the root).
    *   The requirements say:
        *   Higher priority (number) first.
        *   FIFO for same priority.
    *   Since `heapq` is a min-heap, to treat higher numbers as higher priority, I should store priorities as negative numbers (e.g., 2 becomes -2).
    *   To handle FIFO for the same priority, I need a unique counter/sequence number.

    *   `JobQueue` class:
        *   `__init__`: Initialize a list `self._queue` and a counter `self._counter`.
        *   `add_job(job_name, job_data, priority=0)`:
            *   `priority` becomes `-priority`.
            *   Push `(priority, counter, job_name, job_data)` into the heap.
            *   Increment `counter`.
        *   `get_next_job()`:
            *   If heap is empty, return `None`.
            *   Pop from heap.
            *   Return `(job_name, job_data)`.

    *   `queue.add_job("normal", {"type": "normal"}, priority=0)` -> `(-0, 0, "normal", ...)`
    *   `queue.add_job("critical", {"type": "critical"}, priority=2)` -> `(-2, 1, "critical", ...)`
    *   `queue.add_job("high", {"type": "high"}, priority=1)` -> `(-1, 2, "high", ...)`
    *   Heap view (sorted):
        1. `(-2, 1, "critical", ...)`
        2. `(-1, 2, "high", ...)`
        3. `(-0, 0, "normal", ...)`
    *   `get_next_job()` will correctly return "critical" first.

    *   Import `heapq`.
    *   Define the class.
    *    Ensure type hints are used as requested.

    *   Priority 2 (Critical) > Priority 1 (High) > Priority 0 (Normal).
    *   `heapq` pops the smallest. So -2 < -1 < 0. Correct.

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

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

        def add_job(self, job_name: str, job_data: Dict, priority: int = 0):
            # Use negative priority because heapq is a min-heap
            # Use counter to maintain FIFO for same priority
            heapq.heappush(self._queue, (-priority, self._counter, job_name, job_data))
            self._counter += 1

        def get_next_job(self) -> tuple[str, dict] | None:
            if not self._queue:
                return None
            
            _, _, job_name, job_data = heapq.heappop(self._queue)
            return (job_name, job_data)
    ```

    Actually, the prompt asks for `tuple[str, dict] | None`. I should stick to that signature.

    Wait, I should check if the input `priority` is always integer. The prompt says 0, 1, 2. Yes.

'Write only the Python code, no explanations.'

    One detail: `tuple[str, dict] | None` is Python 3.10+ syntax. I'll use that.

    Actually, let's double check the prompt requirements for `get_next_job`.
    `get_next_job() -> tuple[str, dict] | None`

    Implementation check:
    - `add_job` default priority 0? Yes.
    - Higher priority first? Yes (-2 < -1 < 0).
    - Same priority FIFO? Yes (counter incrementing).
    - Returns `None` if empty? Yes.
</reasoning_content>

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

class JobQueue:
    def __init__(self):
        self._queue: list[Tuple[int, int, str, dict]]] = []
        self._counter = 0

    def add_job(self, job_name: str, job_data: Dict, priority: int = 0):
        # Using negative priority because heapq is a min-heap
        # The counter ensures FIFO order for identical priorities
        heapq.heappush(self._queue, (-priority, self._counter, job_name, job_data))
        self._counter += 1

    def get_next_job(self) -> tuple[str, dict] | None:
        if not self._queue:
            return None
        
        _, _, job_name, job_data = heapq.heappop(self._queue)
        return (job_name, job_data)
```
</content>