<reasoning_content>
*   Goal: Implement priority-based job scheduling for a `JobQueue` class.
    *   Requirements:
        1.  `add_job(job_id, data, priority=0)`: Accepts `priority`.
        2.  Higher priority jobs processed first (Highest number = highest priority).
        3.  Same priority = FIFO (First In, First Out).
        4.  `get_next_job()`: Returns the highest priority job as a tuple `(job_id, data)`.
    *   Priority levels: 0 (Normal), 1 (High), 2 (Critical).
    *   Output format: Python code only.

    *   I need a way to store jobs where I can efficiently retrieve the one with the highest priority.
    *   A `heapq` (Min-priority queue) is the standard tool for priority queues.
    *   Since `heapq` is a min-heap (lowest value first), and higher priority values (2 > 1 > 0) should be processed first, I should store priorities as negative numbers (e.g., -2, -1, 0).
    *   To maintain FIFO for same priorities, I need a sequence counter. `heapq` works by comparing elements in a tuple. If the first element (priority) is equal, it compares the second element. If the second element is a counter that increments with every addition, it ensures FIFO.

    *   `self.queue`: A list to be used with `heapq`.
    *   `self.counter`: An integer incremented every time a job is added.
    *   `add_job(job_id, data, priority=0)`:
        *   Push `(-priority, self.counter, job_id, data)` into the heap.
        *   Increment `self.counter`.
    *   `get_next_job()`:
        *   If heap is empty, return `None`.
        *   Pop from heap and return `(job_id, data)`.

    *   `priority=2` -> `-2`
    *   `priority=1` -> `-1`
    *   `priority=0` -> `0`
    *   `heapq` will pop `-2` first, then `-1`, then `0`. This correctly prioritizes higher numbers.
    *   If priorities are same, `self.counter` will break the tie (smaller counter = earlier job).

    ```python
    import heapq

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

        def add_job(self, job_id: str, data: dict, priority: int = 0):
            # We use -priority because heapq is a min-heap
            # and we want higher numbers to come out first.
            # self.counter ensures FIFO for identical priorities.
            heapq.heappush(self.queue, (-priority, self.counter, job_id, data))
            self.counter += 1

        def get_next_job(self) -> tuple[str, dict] | None:
            if not self.queue:
                return None
            priority_neg, counter, job_id, data = heapq.heappop(self.queue)
            return (job_id, data)
    ```

    *   Requirement 1: `add_job` has `priority=0`. Checked.
    *   Requirement 2: Higher priority first. Checked (via negation in min-heap).
    *   Requirement 3: Same priority FIFO. Checked (via counter).
    *   Requirement 4: `get_next_job` returns `tuple` or `None`. Checked.

    *Self-Correction during drafting:* Should I use a simple list and sort it? No, that's $O(N \log N)$ per get or $O(N)$ insertion. Heap is $O(\log N)$ for both, which is optimal.

    Wait, the example shows:
    `queue.add_job("normal", ..., priority=0)`
    `queue.add_job("critical", ..., priority=2)`
    `queue.add_job("high", ..., priority=1)`
    `get_next_job()` -> returns critical.
    My logic `-2, -1, 0` will pop `-2` first. Correct.
</reasoning_content>

<content>
```python
import heapq

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

    def add_job(self, job_id: str, data: dict, priority: int = 0):
        # heapq is a min-heap. To make higher priority values 
        # come out first, we store priority as a negative number.
        # The counter ensures FIFO order for jobs with the same priority.
        heapq.heappush(self.queue, (-priority, self.counter, job_id, data))
        self.counter += 1

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