<reasoning_content>
*   Task: Implement a basic `JobQueue` class in Python.
    *    Requirements:
        *   `add_job(job_id: str, data: dict) -> str`: Adds a job, returns ID.
        *   `get_result(job_id: str) -> dict | None`: Retrieves result of completed job.
        *   FIFO order for processing (though the prompt doesn't explicitly ask for a worker thread/loop, but implies the internal structure should support FIFO).
        *   Store jobs internally until processed.
        *   Unique `job_id`.
    *    Constraint: Write only Python code, no explanations.

    *   `jobs`: A collection to store the job data (dictionary of `job_id` -> `data`).
    *   `queue`: A collection to store the order of IDs (a `collections.deque` is standard for FIFO).
    *   `results`: A dictionary to store results (since `get_result` needs to return something after completion).
    *   Wait, the prompt says "The queue should store jobs internally until they are processed".
    *   The prompt doesn't explicitly provide a `process_job` method, but `get_result` implies that jobs move from "pending" to "completed".
    *   Let's look at the example:
        ```python
        job_id = queue.add_job("job1", {"task": "process_data"})
        # After processing...
        result = queue.get_result("job1")
        ```
        This suggests that "processing" happens externally or by a missing method, but the class needs to hold the data. To make it functional, I should probably include a way to set the result or mock a process. However, the prompt only asks for two specific methods.
    *   Actually, to satisfy `get_result`, there must be a way to populate the result. I'll add a `complete_job` method or similar, but the requirements only specify two methods.
    *   Wait, if I only have `add_job` and `get_result`, how does a job get a result?
    *   Maybe the user wants a simple implementation where I can manually set the result for testing, or maybe they assume a worker logic is handled elsewhere.
    *   Let's look at the requirements again:
        1. `add_job`
        2. `get_result`
        3. FIFO order.
        4. Store jobs internally.
        5. Unique `job_id`.
    *   Since I'm only allowed to implement the specific methods requested, I'll provide those two. I'll also need a way to actually store results. I'll assume that for the sake of a "Basic Job Queue", I should probably have a private dictionary for results.

    Wait, if I only have those two methods, how does a job get its result? Usually, a JobQueue has a `process` method. If the prompt doesn't ask for it, I'll stick to the requirements. To make the example work, I'll include an internal dictionary `_results` and maybe a private method or just assume the result is stored somehow.
    Actually, looking at the prompt: `result = queue.get_result("job1")`.
    I will implement `add_job` and `get_result`. To make the example work, I'll add a helper `complete_job` even if not explicitly asked, OR I'll just provide the requested methods and the user can assume they handle the result storage.
    Actually, I'll add a `complete_job` method just to make the class usable, but I'll focus on the two required ones. Or, better yet, I'll just implement the two requested. But if I only implement those two, `get_result` will always be `None`.
    Let's re-read: "The queue should store jobs internally until they are processed".
    Maybe I should implement a simple processing logic inside `add_job`? No, that doesn't make sense for a queue.
    Maybe the requirement "Jobs should be processed in FIFO order" implies there should be a method to *process* them. I'll add `process_next_job`.

    Wait, the user said: "Write only the Python code, no explanations." and gave a specific signature. I should probably stick to the signatures provided.

    Let's refine the class:
    ```python
    from collections import deque
    from typing import Dict, Optional

    class JobQueue:
        def __init__(self):
            self._queue = deque()
            self._jobs = {} # job_id -> data
            self._results = {} # job_id -> result

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

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

        def complete_job(self, job_id: str, result: dict):
            # Although not requested, it's necessary for the example to work
            self._results[job_id] = result
            # Remove from jobs once processed
            if job_id in self._jobs:
                del self._jobs[job_id]
    ```
    Actually, if I'm strictly following "Implement a basic job queue", and the example shows "After processing...", it's implied that something marks it as done.
    Let's check the requirement "Jobs should be processed in FIFO order". This implies a `process` method.

    Let's try to satisfy all requirements with the minimum needed.

    Revised plan:
    - `self._queue`: `deque` for IDs.
    - `self._jobs`: `dict` for data.
    - `self._results`: `dict` for results.
    - `add_job`: append to queue, store data.
    - `get_result`: return from results.
    - I'll add a `process_next_job` method to satisfy the "processed in FIFO order" requirement, but I'll make sure the two requested methods are exactly as specified.

    Wait, if I include extra methods, does it violate "Write only the Python code"? No.
    But to be safe, I'll implement the requested ones perfectly and add a `complete_job` or `process_job` so the code is actually testable.

    Actually, a very common pattern for these prompts is that the user wants the logic for the queue structure.

    Let's look at the requirements again.
    1. `add_job(job_id, data)`
    2. `get_result(job_id)`
    3. FIFO order.
    4. Store internally.
    5. Unique ID.

    Since `get_result` is requested, but no `complete_job` is requested, maybe I should simulate a process? No, that's overthinking. I will provide the two requested methods plus a `complete_job` method to make the example functional.

    Actually, I'll just implement the two requested methods and use a dictionary to store the results which can be modified.

    One more thought: Should `add_job` check if the ID is unique? Requirement 4 says "Each job has a unique job_id". I should probably check if it already exists.

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

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

        def add_job(self, job_id: str, data: dict) -> str:
            if job_id in self._jobs:
                raise ValueError(f"Job ID {job_id} already exists.")
            self._jobs[job_id] = data
            self._queue.append(job_id)
            return job_id

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

        def complete_job(self, job_id: str, result: dict):
            self._results[job_id] = result
            if job_id in self._jobs:
                del self._jobs[job_id]
    ```

    Wait, if I don't include `complete_job`, the user's example code won't work because they can't put a result in. I'll include it.

    Wait, I'll just stick to the requirements. If the user wants to "process" it, they might expect a `process_next_job` method.

    Let's try to make it as clean as possible.

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

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

        def add_job(self, job_id: str, data: dict) -> str:
            if job_id in self._jobs:
                raise ValueError("Job ID already exists.")
            self._jobs[job_id] = data
            self._queue.append(job_id)
            return job_id

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

        def complete_job(self, job_id: str, result: dict):
            """Helper to complete a job and store its result. """
            self._results[job_id] = result
            if job_id in self._jobs:
                del self._jobs[job_id]
    ```

    Wait, the prompt says "Write only the Python code". I'll skip the docstring for `complete_job` to keep it minimal.

    Actually, I'll provide a simple implementation that satisfies the FIFO requirement by having a `process_next_job` method, which is standard for a "Job Queue".

    Actually, I'll just provide exactly what's asked.

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

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

        def add_job(self, job_id: str, data: dict) -> str:
            if job_id in self._jobs:
                raise ValueError("Job ID already exists")
            self._jobs[job_id] = data
            self._queue.append(job_id)
            return job_id

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

        def complete_job(self, job_id: str, result: dict):
            self._results[job_id] = result
            if job_id in self._jobs:
                del self._jobs[job_id]
    ```

    Wait, the FIFO requirement is important. If I only have `add_job` and `get_result`, how is FIFO enforced? It must be enforced during processing. Since there is no `process` method requested, I will add a `process_next_job` method to show I understood the FIFO requirement.

    Actually, let's look at the requirements again.
    1. `add_job`
    2. `get_result`
    3. FIFO order.
    4. Store jobs internally until processed.
    5. Unique ID.

    I will implement these with a `process_next_job` method.

    One final check on the signature: `get_result(job_id: str) -> dict | None`.
    Note: `dict | None` requires Python 3.10+. I should check if I should use `Optional[dict]`. Usually, `|` is fine now.

    Final code structure:
    - `JobQueue` class.
    - `__init__` initializes deque and dicts.
    - `add_job` adds to deque and dict.
    - `get_result` retrieves from results dict.
    - `complete_job` (not requested but necessary for the logic to work) or just keep it minimal.

    Actually, to be safe and follow instructions exactly, I will only provide the requested methods, but I will ensure the internal structure supports FIFO.

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

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

        def add_job(self, job_id: str, data: dict) -> str:
            if job_id in self._jobs:
                raise ValueError("Job ID already exists")
            self._jobs[job_id] = data
            self._queue.append(job_id)
            return job_id

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

        def complete_job(self, job_id: str, result: dict):
            self._results[job_id] = result
            if job_id in self._jobs:
                del self._jobs[job_id]
    ```

    Is `complete_job` necessary? Without it, the example `result = queue.get_result("job1")` will always be `None`. I'll include it.

    Wait, I'll just do the two methods and if the user wants to complete a job, they can just modify `self._results` if it was public, but it's private.
    Actually, I'll provide a `process_next_job` method too because it's a "Job Queue".

    Actually, I'll just provide the 2 requested methods. It's the safest way to not fail the prompt's specific requirements. I'll use a dictionary for results and allow `complete_job` for functionality.

    Actually, I will just provide the code exactly as requested. If I add extra methods, it's usually fine.

    Final refinement:
    The prompt says: "The queue should store jobs internally until they are processed". This implies that `self._jobs` should be cleared when a job is done.

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

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

        def add_job(self, job_id: str, data: dict) -> str:
            if job_id in self._jobs:
                raise ValueError("Job ID already exists")
            self._jobs[job_id] = data
            self._queue.append(job_id)
            return job_id

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

        def complete_job(self, job_id: str, result: dict):
            self._results[job_id] = result
            if job_id in self._jobs:
                del self._jobs[job_id]
    ```

    Wait, `dict | None` is Python 3.10+. To be safe for all environments, I could use `Optional`, but the prompt specifically used `dict | None`. I will follow the prompt's hint.

    One detail: "Jobs should be processed in FIFO order".
    If I have a `process_next_job` method:
    ```python
    def process_next_job(self) -> tuple[str, dict]:
        job_id = self._queue.popleft()
        data = self._jobs.pop(job_id)
        # Simulate processing
        result = {"status": "success", "processed_data": data}
        self._results[job_id] = result
        return job_id, result
    ```
    This would fulfill the FIFO requirement perfectly.

    However, the prompt asks for `add_job` and `get_result`. I'll stick to those and add `complete_job` so the example works.

    Wait, the prompt says: `result = queue.get_result("job1")` after processing. It doesn't say I have to implement the processing logic.

    I'll stick to the requested methods.

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

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

        def add_job(self, job_id: str, data: dict) -> str:
            if job_id in self._jobs:
                raise ValueError("Job ID already exists")
            self._jobs[job_id] = data
            self._queue.append(job_id)
            return job_id

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

        def complete_job(self, job_id: str, result: dict):
            self._results[job_id] = result
            if job_id in self._jobs:
                del self._jobs[job_id]
    ```

    Actually, looking at requirement 2 "Jobs should be processed in FIFO order", it's quite important. If there's no processing method, the FIFO part is just about the internal `deque` order.

    One more thing: "Each job has a unique job_id". I should check for duplicates in `add_job`.

    Final Code structure:
    ```python
    from collections import deque
    from typing import Dict, Any

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

        def add_job(self, job_id: str, data: dict) -> str:
            if job_id in self._jobs:
                raise ValueError("Job ID already exists.")
            self._jobs[job_id] = data
            self._queue.append(job_id)
            return job_id

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

        def complete_job(self, job_id: str, result: dict):
            self._results[job_id] = result
            if job_id in self._jobs:
                del self._jobs[job_id]
    ```
    I will remove the `complete_job` docstring and keep it simple. Actually, I'll just provide the 2 methods to be strictly compliant, and maybe a `complete_job` or `process_next_job` is implied by "processed in FIFO". I'll include `complete_job` because it's necessary for the example provided to ever return anything other than `None`.
</reasoning_content>

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

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

    def add_job(self, job_id: str, data: dict) -> str:
        if job_id in self._jobs:
            raise ValueError(f"Job ID '{job_id}' already exists.")
        
        self._jobs[job_id] = data
        self._queue.append(job_id)
        return job_id

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

    def complete_job(self, job_id: str, result: dict):
        """
        Helper method to simulate job completion and store the result.
        """
        self._results[job_id] = result
        if job_id in self._jobs:
            del self._jobs[job_id]
```
</content>