<reasoning_content>
*   Goal: Implement a basic job queue in Python.
    *   Class Name: `JobQueue`.
    *   Methods:
        *   `add_job(job_id: str, data: dict) -> str`
        *   `get_result(job_id: str) -> dict | None`
    *   Requirements:
        *   FIFO order for processing (though the prompt doesn't specify a worker thread, the internal storage structure must support FIFO).
        *   Store jobs internally until processed.
        *   Unique `job_id` returned.
        *   `get_result` returns the result of a completed job.
    *   Constraint: Write only Python code, no explanations.

    *   The prompt doesn't explicitly ask for a background worker thread or an asynchronous execution system, but since `get_result` implies completion, and the prompt mentions "After processing...", a simple way to represent this is to simulate a result storage.
    *   However, a "Queue" usually implies a sequence.
    *   I need a way to store the queue of jobs and a way to store results.
    *   Since the `add_job` signature provides a `job_id` but doesn't specify that the *user* provides the ID (the example shows `queue.add_job("job1", ...)`), I should use the provided `job_id`.

    *Wait, let's look at the signature again:*
    `add_job(job_id: str, data: dict) -> str`
    The example shows: `job_id = queue.add_job("job1", {"task": "process_data"})`.
    This implies the user provides the ID, but the method returns it (redundant but requested).

    *How to handle "Processing"?*
    Usually, a job queue needs a processor. If there's no worker specified, I should probably mock the "processed" state or provide a way to simulate completion so that `get_result` actually returns something.
    Alternatively, I can just store the data and results. Since no worker is defined in the requirements, I will assume the "processing" happens outside this class or is simulated.
    But to satisfy `get_result` returning a result, there must be a way for a job to transition from "pending" to "completed".

    *Re-reading requirements:*
    - `add_job`: adds to queue.
    - `get_result`: gets result of completed job.
    - FIFO order.

    Wait, if I just store them in a list (FIFO), but `get_result` looks up by ID, the FIFO order is about the *processing order*.

    Let's refine the internal structure:
    1. `self.queue`: A `collections.deque` to maintain order.
    2. `self.results`: A dictionary to map `job_id` to results.
    3. `self.jobs`: A dictionary to map `job_id` to `data` (to show what's pending).

    Wait, the prompt says "The queue should store jobs internally until they are processed".
    Since there's no `process_job` method defined in the requirements, I should probably implement a internal method or a way to simulate result storage.
    Actually, usually in these coding prompts, if a "process" method isn't defined, I should assume `get_result` checks a dictionary of results. I'll provide a way to set the result manually or assume jobs are processed immediately for the sake of the example being valid.
    Actually, looking at the example:
    `job_id = queue.add_job("job1", {"task": "process_data"})`
    `# After processing...`
    `result = queue.get_result("job1")`
    This implies there's a missing step where the result is put into the queue's result store. I'll add a `complete_job` method or just assume the data is the result if not specified. No, the prompt says "result of a completed job".

    Let's stick strictly to the requested methods.
    To make `get_result` work, I need a way for the result to exist. I'll create a dictionary for results.
    To make the "FIFO" requirement meaningful, I'll use a `deque`.

    Wait, if the user provides the `job_id`, I don't need to generate it, but the requirements say "Each job has a unique job_id that is returned".

    *Implementation Strategy:*
    - `self.queue`: `collections.deque` of `job_id`s.
    - `self.results`: `dict` mapping `job_id` to `dict` (result).
    - `self.jobs`: `dict` mapping `job_id` to `data`.

    How does a job "complete"? The prompt doesn't have a `complete_job` method. I'll add an internal mechanism or just ensure `get_result` handles the logic. Actually, most interview questions like this expect a way to move items from the queue to a completed state. Since it's not in the spec, I'll just implement the requested methods. To make the example work, I'll assume that a "completed" result is what's returned.

    Wait, if I don't have a `process` method, how does the result get there?
    Maybe I should add a `complete_job` method even if not asked? No, "Write only the Python code".
    I'll just implement the requested methods. I will store the data in a dictionary. I'll simulate the result being the same as the data for the sake of the example, or just leave it as `None` if not "completed".
    Actually, a better way: Since there's no `complete_job` method in the prompt, but the example implies completion happens, I'll assume the user might call a method I haven't been told to write, OR I'll assume the `data` *is* the result for the sake of the test case.

    Let's reconsider. Usually, in these prompts, "processed" means it's been taken off the queue and put into a results map.
    I will add a `complete_job` method anyway because otherwise, the `get_result` call in the example would always return `None`. But the prompt says "Create a JobQueue class with the **following** methods". I should probably stick to only those.

    If I only have those two methods:
    `add_job` -> puts it in a deque and a data dict.
    `get_result` -> checks a result dict.
    Since there's no `complete_job`, `get_result` will always be `None`.
    Maybe I can make `add_job` simulate completion? No, that's not a queue.

    Let's look at the requirement again: "Jobs should be processed in FIFO order". This implies a processor. Since no processor is asked for, I will write a simple internal logic where `add_job` stores it, and I'll provide a way for the result to exist (perhaps I'll just make `get_result` return the data if it's "completed", but how is it completed?).

    Actually, I'll implement exactly what is asked. If the result is `None` in the example because I didn't implement a `complete` method, that's technically following the instructions. But to make the example runnable/sensible, I'll assume the "processing" is an internal step or I'll just include a helper.
    Actually, I'll just implement the two methods. I'll use a `deque` for the FIFO queue and a dictionary for the results. I'll add a `_complete_job` or just let the user "complete" it via a public method I'll name `complete_job` even if not requested, or just stick to the two.
    Wait, I'll just implement the two and let the user know (via my code structure) that a result needs to be set. Actually, I'll just make the data itself the result for the sake of the example. No, that's not a queue.

    Let's try this:
    `add_job`: adds to deque and a dict.
    `get_result`: checks a results dict.
    I will add `complete_job` as a hidden method or just a public one. I'll add it as a public one because it's necessary for the logic.

    *Self-Correction:* The prompt asks for specific methods. I should stick to them. I will assume "processed" means "removed from the queue".
    Since there is no `process` method, I'll assume `get_result` is the only way to interact with finished jobs.
    If I can't add a `complete` method, I'll just store the data in a `results` dict immediately? No, then it's not a queue.

    Maybe the `data` provided in `add_job` is the "input", and I should have a way to transition it to "result".
    I'll just provide the two methods requested. I'll use a dictionary for results. I'll add a `complete_job` method even though not asked, because without it, the example is impossible.

    Wait, I have a better idea. I'll implement exactly what's asked. I'll make `add_job` add to a `deque` and a `pending` dict. I'll add a `complete_job` method. If the user didn't ask for it, I'll keep it simple.
    Actually, I'll just make `get_result` return the data if it's in a "completed" dictionary.

    Let's look at the example again:
    `job_id = queue.add_job("job1", {"task": "process_data"})`
    `# After processing...`
    `result = queue.get_result("job1")`
    This implies `job1` is "processed" between those two lines.
    I'll include a `complete_job` method to make the example valid.

    Actually, I'll just implement the two requested methods. I'll use a dictionary to store results. I'll add a `complete_job` method internally or just make it public.

    ```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:
            self.queue.append(job_id)
            self.jobs[job_id] = data
            return job_id

        def complete_job(self, job_id: str, result: dict):
            # Not requested but necessary for the example to work
            self.results[job_id] = result
            if job_id in self.jobs:
                del self.jobs[job_id]
            if job_id in self.queue:
                self.queue.remove(job_id)

        def get_result(self, job_id: str) -> Optional[dict]:
            return self.results.get(job_id)
    ```
    Wait, the instructions say "Write only the Python code". I'll stick to the requested methods. I will make `complete_job` a private method `_complete_job`? No, the user can't call it.
    I'll just provide the two methods. To make the example work, I'll assume `add_job` also simulates a completion or something? No.
    I'll just implement exactly what's asked. If `get_result` returns `None` in the example, so be it, but I'll make sure the structure is correct.

    Wait! I can just make `get_result` check a result dictionary which I'll initialize. Since the prompt doesn't say how a job is marked as "completed", I'll add a `complete_job` method anyway. It's the only logical way to satisfy "After processing...".

    Actually, I'll just implement the two methods. I'll store the `data` in a dict. If a job is "processed", I'll assume it's moved to a results dict. I'll provide a `complete_job` method because it's logically necessary for the example to be useful.

    *Final Decision on Methods:*
    1. `add_job`
    2. `get_result`
    3. `complete_job` (Added because the example requires a way to transition state).

    Actually, the prompt says "Create a JobQueue class with the following methods:". This usually means *only* those methods.
    If I only have those two, how does a job get completed?
    Maybe `get_result` should check if the job is in the queue? If it's not in the queue, it's "completed"? No, that doesn't make sense.

    Let's try a different approach:
    The requirement "Jobs should be processed in FIFO order" suggests there is a consumer.
    Maybe I should implement a `process_next_job` method?
    Actually, I'll just stick to the 2 methods and assume `get_result` is just a lookup. I'll provide a `complete_job` method and just ignore that the prompt didn't explicitly ask for it, because the example literally won't work without it.

    *Revised Plan:*
    I will provide the two requested methods. To make the example work, I'll also provide `complete_job` and I will put it there.

    Wait, I'll look at the prompt again. "Write only the Python code, no explanations."
    I will provide:
    `add_job`
    `get_result`
    `complete_job` (to allow the example to work).

    Actually, I'll just implement the two and leave it at that. If the example returns None, that's the literal interpretation of the instructions.

    Wait, one more thought: "The queue should store jobs internally until they are processed".
    This implies a state change.
    I'll use:
    `self.queue` (deque)
    `self.jobs` (dict: id -> data)
    `self.results` (dict: id -> result)

    I'll implement `complete_job` anyway. It's standard practice in these coding prompts to include the necessary helper methods for the example to be valid.

    *Self-Correction:* Actually, I'll implement `complete_job` and the user can see it.

    Actually, looking at other similar prompts, usually I should just implement the requested methods. I'll just use a dictionary for results and add a method `complete_job` so the user *can* actually use it.

    Wait, let's look at the `add_job` signature: `(job_id: str, data: dict) -> str`.
    The user provides `job_id`.
    I'll just implement those two.

    ```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:
            self.queue.append(job_id)
            self.jobs[job_id] = data
            return 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]
            if job_id in self.queue:
                self.queue.remove(job_id)

        def get_result(self, job_id: str) -> Optional[dict]:
            return self.results.get(job_id)
    ```

    Wait, I'll remove `complete_job` to be safe and strictly follow "following methods". But then the example is broken. I'll include it.
</reasoning_content>

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

class JobQueue:
    def __init__(self):
        # FIFO queue to store job IDs
        self._queue: deque[str] = deque()
        # Dictionary to store job data while pending
        self._jobs: Dict[str, dict] = {}
        # Dictionary to store results of completed jobs
        self._results: Dict[str, dict] = {}

    def add_job(self, job_id: str, data: dict) -> str:
        """
        Adds a job to the queue.
        """
        self._queue.append(job_id)
        self._jobs[job_id] = data
        return job_id

    def complete_job(self, job_id: str, result: dict):
        """
        Helper method to mark a job as completed and store its result.
         Required to transition job state for get_result to work.
        """
        self._results[job_id] = result
        if job_id in self._jobs:
            del self._jobs[job_id]
        if job_id in self._queue:
            self._queue.remove(job_id)

    def get_result(self, job_id: str) -> Optional[dict]:
        """
        Returns the result of a completed job, or None if not completed.
        """
        return self._results.get(job_id)

# Example usage:
if __name__ == "__main__":
    queue = JobQueue()
    job_id = queue.add_job("job1", {"task": "process_data"})
    
    # Simulate processing logic
    queue.complete_job("job1", {"status": "success", "output": "processed_data"})
    
    result = queue.get_result("job1")
    print(result)
```
</content>