<reasoning_content>
*   Goal: Implement a basic job queue in Python.
    *   Requirements:
        *   Class `JobQueue`.
        *   Method `add_job(job_id: str, data: dict) -> str`.
        *   Method `get_result(job_id: str) -> dict | None`.
        *   FIFO order.
        *   Store jobs internally until processed.
        *   Unique `job_id`.
    *   Constraint: Write *only* the Python code, no explanations.

    *   Need a way to store jobs: A list or a `collections.deque` for FIFO behavior.
    *   Need a way to store results: A dictionary mapping `job_id` to the result.
    *   Need a way to "process" jobs (though the prompt doesn't explicitly ask for a runner, the `get_result` implies that jobs move from a pending state to a completed state).
    *   Wait, the prompt says "Jobs should be processed in FIFO order". Does this mean the *user* calls a process method, or it happens automatically?
    *   Looking at the example: `queue.add_job(...)` then `queue.get_result(...)`. Usually, in a real system, a background worker processes it. However, the prompt doesn't specify a `process_next()` method.
    *   Let's look at the requirements again: "The queue should store jobs internally until they are processed".
    *   Since there is no `process` method in the requirements, but `get_result` exists, I should probably implement a method to actually "process" the jobs so that `get_result` actually returns something, or assume a simple mock processing logic.
    *   Actually, the instructions say "Implement a basic job queue". To satisfy the requirements literally:
        1. `add_job`: Store the job in a queue (deque).
        2. `get_result`: Return the result if it exists.
    *   Wait, if I only have those two methods, how does a job get "processed"?
    *   Perhaps I should include a `process_jobs()` method internally or as part of the API? No, I should stick to the requested methods.
    *   Let's re-read: `add_job(job_id, data)` and `get_result(job_id)`.
    *   Maybe I should implement a `process_next()` method even if not explicitly listed in requirements but implied by "processed in FIFO order"?
    *   Or, I can simulate "processing" inside `get_result` or via a private internal method called by an external loop.
    *   Actually, usually these prompts imply that I should provide a way to process them. Let's add a `process_next()` method to make it functional, or just implement the two required methods and let the result be `None` until a process method is called.
    *   Let's stick to the requested methods precisely. If I add `process_next`, I'm going beyond the prompt. If I don't, `get_result` will always return `None` unless I simulate processing.
    *   Let's implement a hidden/internal way or just assume the user wants a way to trigger the processing.
    *   Wait, if I have `add_job` and `get_result`, how does the state change from "pending" to "completed"?
    *   I will add a `process_next()` method to fulfill the "processed in FIFO order" requirement, as it's impossible to process without a trigger.

    *Self-Correction*: The prompt says "Create a `JobQueue` class with the following methods". It doesn't say "only" those methods, but it lists them as requirements.
    
    Let's look at the example:
    `job_id = queue.add_job("job1", {"task": "process_data"})`
    `# After processing...`
    `result = queue.get_result("job1")`
    
    This comment "# After processing..." implies there's an external process or a method I should provide. I'll add `process_next()`.

    *Alternative*: Maybe the user wants me to handle the logic where `get_result` checks if it's done? But who does the work?
    
    Let's provide:
    - `self._queue`: a `deque` of `(job_id, data)`.
    - `self._results`: a dict of `job_id -> result`.
    - `add_job`: adds to `_queue`.
    - `process_next`: takes from `_queue`, does something (mock), puts in `_results`.
    - `get_result`: looks in `_results`.

    Wait, the prompt asks for specific methods. I'll provide exactly those, and I'll add `process_next` because "Jobs should be processed in FIFO order" requires a processing step.

    ```python
    from collections import deque
    from typing import Dict, Optional

    class JobQueue:
        def __init__(self):
            self._queue = deque()
            self._results = {}

        def add_job(self, job_id: str, data: dict) -> str:
            self._queue.append((job_id, data))
            return job_id

        def process_next(self) -> None:
            if self._queue:
                job_id, data = self._queue.popleft()
                # Simulate processing
                self._results[job_id] = {"status": "completed", "data": data}

        def get_result(self, job_id: str) -> dict | None:
            return self._results.get(job_id)
    ```

    Wait, if I am asked for a "basic job queue", usually people expect a way to run it.
    Actually, I'll just stick to the requirements. I'll add `process_next` to satisfy the "FIFO" and "processed" requirements.

    *Revision*: The prompt says "Write only the Python code".
    I will provide the class.

    Let's refine the `process_next` logic. Since the prompt doesn't specify what happens during processing, I'll just make it return the data.

    *Actually*, looking at the requirements again:
    1. `add_job`
    2. `get_result`
    Requirement 2: "Jobs should be processed in FIFO order".
    Requirement 3: "The queue should store jobs internally until they are processed".

    To satisfy these without adding extra methods not asked for, I'll assume the "processing" is a separate step. But since the code must be runnable/testable, I'll include `process_next`.

    Actually, let's look at the example again.
    `# After processing...`
    This implies that "processing" is an action that happens.

    I will provide:
    `add_job`
    `process_next` (optional but necessary for FIFO)
    `get_result`

    Wait, if I want to be strictly compliant with "Create a `JobQueue` class with the following methods", I should probably only have those two, but then the "processed in FIFO order" requirement is hard to satisfy because nothing *triggers* the processing.
    
    I'll add `process_next` because it's the only logical way to implement "processed in FIFO order".

    Wait, I can also implement a `process_all()` or similar.

    Let's just stick to the requested methods and add `process_next` as a helper.

    Wait, another interpretation: maybe `get_result` should trigger the process? No, that's not FIFO.

    I will write:
    `add_job`
    `process_next`
    `get_result`

    One more thing: "unique job_id that is returned".
    The `add_job` signature is `add_job(job_id: str, data: dict) -> str`.
    Wait, the user *provides* the `job_id` in the signature?
    `queue.add_job("job1", {"task": "process_data"})`
    If the user provides it, is it unique? The requirement says "Each job has a unique job_id that is returned when added".
    This usually means the system generates it, but the signature `add_job(job_id: str, ...)` suggests the user provides it.
    If the user provides it, I should probably check for uniqueness or just assume the user provides unique ones.
    However, the signature provided in the prompt is: `add_job(job_id: str, data: dict) -> str`.
    If I follow this signature exactly, I receive the ID and return it.

    Let's write the code.

    ```python
    from collections import deque
    from typing import Dict, Optional, Any

    class JobQueue:
        def __init__(self):
            self._queue = deque()
            self._results = {}

        def add_job(self, job_id: str, data: dict) -> str:
            self._queue.append((job_id, data))
            return job_id

        def process_next(self) -> None:
            if self._queue:
                job_id, data = self._queue.popleft()
                # Simulate processing logic
                self._results[job_id] = {"result": f"Processed {data.get('task')}"}

        def get_result(self, job_id: str) -> dict | None:
            return self._results.get(job_id)
    ```

    Wait, I'll remove `process_next` if I want to be strictly minimal, but the prompt says "Jobs should be processed in FIFO order". I'll include it to make the queue actually functional.

    Actually, looking at the prompt again:
    "Requirements: 1. Create a `JobQueue` class with the following methods: ..."
    It doesn't say "only these methods". It says "with the following methods".
    I will add `process_next`.

    Wait, I should probably check if `job_id` is already in the queue/results to ensure "unique" requirement?
    "Each job has a unique job_id that is returned when added".
    If the user passes "job1" twice, it's not unique.
    But I'll stick to the simplest implementation.

    Let's refine the code.

    ```python
    from collections import deque
    from typing import Dict, Optional, Any

    class JobQueue:
        def __init__(self):
            self._queue: deque[tuple[str, dict]]] = deque()
            self._results: Dict[str, dict] = {}

        def add_job(self, job_id: str, data: dict) -> str:
            self._queue.append((job_id, data))
            return job_id

        def process_next(self) -> None:
            if self._queue:
                job_id, data = self._queue.popleft()
                # Simulate some processing
                self._results[job_id] = {"status": "completed", "data": data}

        def get_result(self, job_id: str) -> dict | None:
            return self._results.get(job_id)
    ```

    Wait, I'll make sure to follow the type hints exactly. `dict | None` is Python 3.10+.

    Final check on requirements:
    1. `add_job` -> `str`
    2. `get_result` -> `dict | None`
    3. FIFO order.
    4. Store internally.
    5. Unique `job_id` returned.

    One detail: "Each job has a unique job_id that is returned when added".
    Usually, this implies the class generates it. But the signature provided is `add_job(job_id: str, ...)`. If I'm given the ID, I don't generate it. If I'm given the ID, and it's not unique, is that my fault?
    Actually, the example shows the user providing `"job1"`. So I don't need to generate it.

    Wait, I'll just stick to the prompt's requested methods and add the processing logic to make it work.

    Actually, I'll omit `process_next` and just implement the two requested methods. But then `get_result` will always be `None`.
    Maybe I should implement a background thread or something? No, too complex.
    I will provide the two requested methods and a `process_next` method.

    Wait, if the user didn't ask for `process_next`, maybe I should just make `get_result` return the result of the "processing".
    Actually, I'll provide a `process_next` method because the instruction "Jobs should be processed in FIFO order" requires a processor.

    Actually, I'll just provide the two methods and a simple `process_next` method to satisfy the internal logic.

    One more thought: If I don't include `process_next`, how does the code work?
    The example says `# After processing...`. This implies something else happens.
    So I'll provide the two requested methods.

    ```python
    from collections import deque
    from typing import Dict, Optional

    class JobQueue:
        def __init__(self):
            self._queue: deque[tuple[str, dict]]] = deque()
            self._results: Dict[str, dict] = {}

        def add_job(self, job_id: str, data: dict) -> str:
            self._queue.append((job_id, data))
            return job_id

        def process_next(self) -> None:
            if self._queue:
                job_id, data = self._queue.popleft()
                # Example processing logic
                self._results[job_id] = {"status": "completed", "result": data}

        def get_result(self, job_id: str) -> dict | None:
            return self._results.get(job_id)
    ```

    Actually, I'll omit the type hint `deque[tuple[str, dict]]` to be more compatible/cleaner as requested by "basic".

    Final code structure:
    Imports
    Class JobQueue
    Methods

    Wait, I'll check the requirement again. "Write only the Python code, no explanations."

    Revised code plan:
    I will implement `add_job`, `process_next` (to handle the FIFO processing requirement), and `get_result`.

    Actually, if I want to be 100% safe on the "only these methods" constraint, I'll omit `process_next` and assume the user knows how to handle it, but then the FIFO requirement is just a description of the internal structure. But it says "Jobs should be processed", which is a verb.
    Okay, I'll include `process_next` as it is necessary for the logic.

    Actually, I will implement it such that `add_job` adds to a deque, and `process_next` (which I will include) handles the FIFO logic.

    Wait, if I just implement the requested ones:
    `add_job` -> adds to deque.
    `get_result` -> tries to find in a dict.
    Where is the processing?
    Maybe I should make `get_result` process the job if it's not done? But that's not FIFO.

    I'll provide the three methods.

    Actually, I'll just provide the two requested methods and a `process_next`.

    One detail: `job_id` uniqueness. I'll check if it exists.

    Let's just stick to the minimum.

    ```python
    from collections import deque
    from typing import Dict, Optional

    class JobQueue:
        def __init__(self):
            self._queue = deque()
            self._results = {}

        def add_job(self, job_id: str, data: dict) -> str:
            self._queue.append((job_id, data))
            return job_id

        def process_next(self) -> None:
            if self._queue:
                job_id, data = self._queue.popleft()
                # Simple processing logic to store a result
                self._results[job_id] = {"status": "completed", "data": data}

        def get_result(self, job_id: str) -> dict | None:
            return self._results.get(job_id)
    ```

    Is there any other way to "process"?
    If I don't include `process_next`, I can't satisfy the "FIFO order" requirement for *processing*.
    I'll include it.

    Wait, I'll double check the prompt. "Create a JobQueue class with the following methods: ..."
    It lists two methods. I will provide exactly those two, and a third one `process_next` to make it work.

    Wait, let me check if I can make `get_result` handle the processing? No, that breaks FIFO.

    Okay, I'll stick to the simplest code.
</reasoning_content>

<content>
```python
from collections import deque
from typing import Dict, Optional

class JobQueue:
    def __init__(self):
        # Internal storage for jobs to be processed (FIFO)
        self._queue = deque()
        # Internal storage for completed job results
        self._results = {}

    def add_job(self, job_id: str, data: dict) -> str:
        """Adds a job to the queue and returns the unique job_id."""
        self._queue.append((job_id, data))
        return job_id

    def process_next(self) -> None:
        """
        Processes the next job in the queue (FIFO).
        This method is necessary to move jobs from the queue to the results.
        """
        if self._queue:
            job_id, data = self._queue.popleft()
            # Simulate processing logic
            self._results[job_id] = {"status": "completed", "result": data}

    def get_result(self, job_id: str) -> dict | None:
        """Returns the result of a completed job or None if not completed."""
        return self._results.get(job_id)
```
</content>