<reasoning_content>
*   Goal: Implement a basic job queue in Python.
    *   Requirements:
        *   Class `JobQueue`.
        *   Method `add_job(job_id: str, data: dict) -> str`: Add a job, return `job_id`.
        *   Method `get_result(job_id: str) -> dict | None`: Get result of completed job.
        *   Order: FIFO (First-In-First-Out).
        *   Storage: Jobs stored internally until processed.
        *   Uniqueness: Each job has a unique `job_id`.
    *   Constraints: Only Python code, no explanations.

    *   Need a way to store the queue of jobs (FIFO). `collections.deque` is standard for this.
    *   Need a way to store job results by `job_id`. A dictionary `self.results` will work.
    *   Need a way to store the actual data/status of the jobs to "process" them. Since the prompt doesn't specify *how* processing happens (e.g., worker threads, async, or manual trigger), but says "until they are processed", I should probably provide a mechanism to process them or simulate processing to allow `get_result` to work.
    *   *Self-Correction on Processing:* The prompt asks for a "basic job queue" and implies a mechanism where jobs stay in the queue until processed. Usually, a queue class might have a `process_jobs()` method or use a worker. However, looking at the example: `queue.add_job` happens, then "After processing...", `queue.get_result` happens. This implies there needs to be a way to mark a job as "processed" with a result.
    *   *Refined Plan:*
        1.  `self.queue`: a `deque` of `job_id`s.
        2.  `self.jobs`: a dict mapping `job_id` to the data.
        3.  `self.results`: a dict mapping `job_id` to the output.
        4.  `add_job`: push to queue and store data.
        5.  `process_next_job`: (internal or helper) to move from queue to results. Since the prompt doesn't explicitly ask for a `process_jobs` method, but `get_result` depends on completion, I'll include a simple `process_next_job` or similar to fulfill the "processed" state. Wait, looking at the requirement `get_result(job_id: str) -> dict | None`, it implies the result is stored there once done.
        6.  Let's look at the requirements again: "The queue should store jobs internally until they are processed". This suggests a list/deque of jobs.
        7.  To make the example usage valid, I need a way to "process" the job. I'll add a `process_job` method or similar to actually transition the status.

    *   *Revised Structure:*
        *   `self.queue`: `deque` of `job_id`.
        *   `self.jobs`: dict of `job_id` -> `data`.
        *   `self.results`: dict of `job_id` -> `result_data`.
        *   `add_job`: add to `queue` and `jobs`.
        *   `process_job`: take from `queue`, simulate/perform work, put into `results`.
        *   `get_result`: look up in `results`.

    *   *Wait, the prompt doesn't ask for a `process_job` method in the class definition.* It only asks for `add_job` and `get_result`. If I don't provide a way to process, `get_result` will always be `None`.
    *   To make the example work, I'll assume a simple internal process or provide a way to simulate completion. Let's assume the user wants the infrastructure to support this.
    *   Actually, to be strictly compliant with the prompt's method signatures:
        `add_job(job_id: str, data: dict) -> str`
        `get_result(job_id: str) -> dict | None`
    *   I'll implement a `process_next_job` method internally or as a public method to satisfy the "until they are processed" requirement.

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

        def process_jobs(self):
            # To make the example work, I need a way to actually "process"
            # Since the prompt doesn't specify a method, I'll add one
            # or just implement the required ones.
            while self.queue:
                jid = self.queue.popleft()
                data = self.jobs.pop(jid)
                # Simulate processing
                self.results[jid] = {"status": "completed", "original_data": data}

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

    Wait, the prompt says "Write only the Python code". I should probably stick strictly to the requested methods but ensure the logic exists. To make the example usage work, I need to actually process the job. I'll add a `process_next_job` method.

    Actually, I'll just implement exactly what's asked. If the user wants to see it work, they can call a processing method.

    *Constraint Check:*
    - `add_job(job_id: str, data: dict) -> str`
    - `get_result(job_id: str) -> dict | None`
    - FIFO order.
    - Unique `job_id` (the user provides it in the signature, but I should ensure it's stored).

    Actually, in the example `queue.add_job("job1", ...)`, the user provides the ID. I should just store it.

    Let's refine the internal state:
    `self.queue`: a `deque` of `job_id`s.
    `self.job_data`: dict `job_id` -> `data`
    `self.results`: dict `job_id` -> `result`

    To satisfy "until they are processed", I'll add a `process_next_job` method to simulate the logic.

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

    class JobQueue:
        def __init__(self):
            self.queue = deque()
            self.job_data = {}
            self.results = {}

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

        def process_next_job(self) -> None:
            """Helper to simulate processing to make the example functional """
            if self.queue:
                job_id = self.queue.popleft()
                data = self.job_data.pop(job_id)
                # Simulate some work
                self.results[job_id] = {"result": f"Processed {data.get('task')}"}

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

    Wait, the instructions don't ask for `process_next_job`. But without it, `get_result` is always None. I will include it so the example usage works as intended.

    Actually, looking at the prompt: "The queue should store jobs internally until they are processed". This implies a state change.

    Let's provide a clean implementation.

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

    class JobQueue:
        def __init__(self):
            self._queue = deque()
            self._jobs = {}      # Stores job data while in queue
            self._results = {}   # Stores results after processing

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

        def process_next_job(self) -> None:
            """
            Processes the next job in the queue.
            This method is necessary to move a job from 'pending' to 'completed'.
            """
            if self._queue:
                job_id = self._queue.popleft()
                job_data = self._jobs.pop(job_id)
                # Simulate processing logic
                self._results[job_id] = {"status": "completed", "data": job_data}

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

    Actually, I'll keep it even simpler to match the prompt exactly.

    - `add_job`
    - `get_result`
    - FIFO order.

    One final check: "The queue should store jobs internally until they are processed".
    This means I should have a list of jobs.

    Final Code Structure:
    `__init__`: `self.queue = deque()`, `self.results = {}`, `self.jobs = {}`
    `add_job`: append to `queue`, store in `jobs`.
    `process_next_job`: pop from `queue`, move from `jobs` to `results`. (I'll add this to make it functional).
    `get_result`: return from `results`.

    Wait, I'll check if I should include `process_next_job`. The prompt doesn't ask for it, but the example implies it happens. I'll include a `process_jobs` or `process_next_job`.

    Wait, I should probably just implement the two required methods and a simple way to process.

    Actually, I'll implement exactly what's asked. If I don't provide a way to process, the user can't see a result. I'll add `process_next_job` as an internal-like helper or public method.

    One more thought: Is there a way to "process" without a method? Maybe the user expects the `get_result` call to trigger processing? No, that's not how a queue works. A queue usually has a worker.

    I will provide the two required methods and a `process_next_job` method to satisfy the "processed" part of the logic.

    Actually, I'll stick to the prompt's requested methods only, and maybe add a `complete_job` method to move it to results.

    Actually, I'll just provide a `process_next_job` method. It makes the class useful.

    ```python
    from collections import deque
    from typing import Dict, 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:
            self._queue.append(job_id)
            self._jobs[job_id] = data
            return job_id

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

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

    Wait, the requirements say:
    1. `add_job`
    2. `get_result`

    I will stick to those two and provide a way to process inside `get_result`? No, that's bad design. I'll provide the two required methods and one extra one to make it work.

    Actually, looking at the instructions "Write only the Python code", I'll provide the requested class.

    Revised plan:
    - `add_job` -> adds to `deque`.
    - `get_result` -> if `job_id` is in `results`, return it.
    - To make it "processed", I'll add a `complete_job` or `process_next_job`.

    Wait, if I don't have a way to process, the `results` dict will always be empty.
    I'll add `process_next_job`.

    Actually, let's look at the phrasing: "The queue should store jobs internally until they are processed".
    This suggests a `process_jobs` or similar loop.

    I'll provide the two required methods and a `process_next_job` method.

    Final check on prompt: "Implement a basic job queue".
    I'll just write a clean version.

    ```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 process_next_job(self) -> None:
            if self.queue:
                job_id = self.queue.popleft()
                job_data = self.jobs.pop(job_id)
                # Simulated processing
                self.results[job_id] = {"status": "completed", "original_data": job_data}

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

    Wait, I'll remove the `process_next_job` method if I want to be strictly compliant, but then the example won't work. I'll include it because "processed" is a requirement.

    Actually, I'll make `get_result` handle the "processing" if it hasn't been done yet? No, that violates FIFO.

    I will stick to:
    1. `add_job`
    2. `get_result`
    3. `process_next_job` (as a helper to make the requirement "until they are processed" actually mean something).

    Wait, I'll just write the two methods. If the user wants to "process" it, they might assume there's a worker. I'll write a simple internal list for results.

    Actually, the prompt says "The queue should store jobs internally until they are processed".
    This implies there's a state where they are "in the queue" and then "processed".

    Final code:
    ```python
    from collections import deque
    from typing import Dict, Optional

    class JobQueue:
        def __init__(self):
            self._queue = deque()
            self._jobs = {}        # Stores job data while waiting
            self._results = {}     # Stores results after processing

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

        def process_next_job(self) -> None:
            """
            Moves the next job from the queue to the results dictionary.
            """
            if self._queue:
                job_id = self._queue.popleft()
                job_data = self._jobs.pop(job_id)
                # Simulate logic/processing
                self._results[job_id] = {"status": "completed", "data": job_data}

        def get_result(self, job_id: str) -> Optional[dict]]:
            return self._results.get(job_id)
    ```
    Wait, the prompt says "Write only the Python code, no explanations."

    I'll just output the class.
</reasoning_content>

<content>
```python
from collections import deque
from typing import Dict, 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:
        """Adds a job to the queue and returns the job_id."""
        self._queue.append(job_id)
        self._jobs[job_id] = data
        return job_id

    def process_next_job(self) -> None:
        """
        Processes the next job in the FIFO queue.
        This moves the job from the internal queue to the results dictionary.
        """
        if self._queue:
            job_id = self._queue.popleft()
            job_data = self._jobs.pop(job_id)
            # Simulate processing logic
            self._results[job_id] = {
                "status": "completed",
                "data": job_data
            }

    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)
```
</content>