<reasoning_content>
*   Target: A basic job queue in Python.
    *   Class: `JobQueue`.
    *   Methods:
        *   `add_job(job_id: str, data: dict) -> str`: Adds a job, returns ID.
        *   `get_result(job_id: str) -> dict | None`: Returns result or None.
    *   Processing logic: FIFO order.
    *   Storage: Internal storage for jobs and results.
    *   Note: The prompt asks for a *job queue* but doesn't explicitly ask for a worker/processor thread. However, for "get_result" to work with a "completed job", there needs to be a mechanism to process them. A simple synchronous version might just store them, but usually, a "queue" implies processing. Looking at the requirement "Jobs should be processed in FIFO order", it implies a worker needs to run or there's a `process_jobs` method.
    *   *Correction:* The prompt doesn't define a `process` method or a worker. It only asks for `add_job` and `get_result`. To make it functional for a test, I should probably include a way to simulate processing or provide a simple way to trigger processing so `get_result` actually returns something. But strictly following the instructions, I should only implement the requested methods. Wait, if I only implement those two, the job stays in the queue forever and `get_result` always returns `None`.
    *   Let's look at the example: `job_id = queue.add_job(...) # After processing... result = queue.get_result(...)`. This implies processing happens externally or via some mechanism.
    *   To satisfy "Jobs should be processed in FIFO order", I'll implement a simple `_process_next_job` method or just assume there's a background process. However, since I'm writing a class, I'll add a `process_next_job` method to make the code actually usable/testable, or even better, I'll implement it such that a call to a method (like `process_jobs`) handles the FIFO requirement.
    *   Actually, the most "standard" way to implement this without being asked for threading is to have a `process_jobs()` method or similar. But the requirements *only* list `add_job` and `get_result`.
    *   Wait, if I want to follow the prompt *strictly*:
        1. `add_job`
        2. `get_result`
    *   How do jobs get processed? I'll add a `process_job` method to make it a complete implementation, even if not explicitly requested, because without it, the "FIFO" and "completed job" requirements are impossible to demonstrate. Or, I can make `add_job` trigger a (mock) processing.
    *   Let's look at requirement 2: "Jobs should be processed in FIFO order".
    *   Let's look at requirement 3: "The queue should store jobs internally until they are processed".
    *   A typical way to do this in a single-threaded context for a coding challenge is to have a `process_next_job` or just have a `process_all_jobs` method.
    *   Let's refine the implementation:
        *   `self.queue`: a `collections.deque` for FIFO.
        *   `self.results`: a `dict` to store results by `job_id`.
        *   `self.jobs`: a `dict` to store job data (though not strictly needed if we just use the queue for IDs).
        *   I will add a `process_next_job` method so the logic can be demonstrated.

    *   Refined approach:
        `add_job` adds to `deque`.
        `process_next_job` pops from `deque`, simulates work, adds to `results`.
        `get_result` checks `results`.

    *   Wait, looking at the requirements again: "Each job has a unique job_id that is returned when added". This means the caller provides the `job_id`.

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

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

            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) -> Optional[dict]:
                return self._results.get(job_id)

            def process_next_job(self):
                if not self._queue:
                    return
                job_id = self._queue.popleft()
                data = self._jobs.pop(job_id)
                # Simulate processing
                result = {"status": "completed", "original_data": data}
                self._results[job_id] = result
        ```

    *   Actually, the requirement doesn't ask for a `process_next_job` method. However, the instructions say "Jobs should be processed in FIFO order". If I don't provide a way to process them, the class is useless. I will include a `process_jobs` method or similar to ensure the requirement is met in a way that makes the code valid.

    *   Let's check requirement 4: "Each job has a unique job_id that is returned when added". The method signature provided is `add_job(job_id: str, data: dict) -> str`.

    *   Final plan:
        1. `collections.deque` for the queue.
        2. `dict` for storing results.
        3. `dict` for storing pending jobs.
        4. `process_next_job` (to fulfill the "processed" logic requirement).

    *   Wait, looking at the example: `job_id = queue.add_job("job1", {"task": "process_data"})`. The `job_id` is passed *into* the function. This is slightly unusual (usually the system generates the ID), but I will follow the signature.

    *   Wait, if I implement `process_next_job`, I am adding a method not in the requirements. If I don't, I can't fulfill the requirement of jobs being "processed".
    *   Actually, maybe the "processing" is intended to be done by a separate thread or by a method I don't call?
    *   Let's look at the prompt's "Example usage". It says `# After processing...`. This implies the processing happens *between* the `add_job` and `get_result` calls.
    *   I'll include a simple `process_next_job` method.

    *   Wait, looking at the prompt again: "Write only the Python code, no explanations."

    *   Implementation:
        ```python
        from collections import deque
        from typing import Dict, Any, Optional

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

            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) -> Optional[dict]:
                return self._results.get(job_id)

            def process_next_job(self):
                if not self._queue:
                    return
                
                job_id = self._queue.popleft()
                job_data = self._jobs.pop(job_id)
                
                # Simulated processing: return the data back with a success flag
                self._results[job_id] = {"status": "success", "data": job_data}
        ```

    *   Let's check if `job_id` is already in queue to prevent duplicates? The prompt says "Each job has a unique job_id", implying the caller guarantees uniqueness or the system handles it. I'll assume uniqueness is handled.

    *   One more detail: `dict | None` is Python 3.10+ syntax. I'll use it.

    *   Wait, if I want to be very clever, I could make `add_job` perform the work immediately (synchronous), but that's not a "queue". A queue implies a separation of adding and processing.

    *   Let's stick to the provided requirements and add the `process_next_job` to make it testable/complete.

    *   Final check of the requirements:
        1. `add_job` returns `job_id`.
        2. `get_result` returns `dict | None`.
        3. FIFO.
        4. Store until processed.
        5. Unique job_id.

    *   Let's write the code.

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

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

        def add_job(self, job_id: str, data: dict) -> str:
            self._pending_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 process_next_job(self) -> None:
            """Processes the next job in the queue in FIFO order."""
            if not self._queue:
                return

            job_id = self._queue.popleft()
            data = self._pending_jobs.pop(job_id)
            
            # Simulate processing by returning the data in a result dict
            self._results[job_id] = {"status": "completed", "data": data}
    ```

    *   Actually, to be safe and keep it strictly to the prompt, I'll implement just the requested methods and the processing mechanism required to make them work. Since the prompt mentions "Jobs should be processed in FIFO order", and doesn't provide a processing method, I will add a `process_job` method.

    *   Final structure check:
        - `add_job` (matches signature)
        - `get_result` (matches signature)
        - `_queue` (internal)
        - `_results` (internal)
        - `_jobs` (internal)
        - Logic: FIFO via `deque.popleft()`.

    *   Final decision on the extra method: I will add `process_next_job` because without it, "processed" is an undefined state.

    *   One small refinement: The prompt asks for `dict | None` in the signature. In Python 3.10+, this is valid.

    *   Wait, should I use `threading`? The prompt is "basic job queue". Adding threading might overcomplicate it. A simple `process_next_job` is enough to demonstrate the FIFO logic.

    *   Let's refine the code one last time.

    ```python
    from collections import deque

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

        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 process_next_job(self):
            if not self._queue:
                return
            
            job_id = self._queue.popleft()
            data = self._jobs.pop(job_id)
            
            # Simulating work
            self._results[job_id] = {"result": "processed", "input": data}
    ```

    *   This looks perfect.

    *   Wait, one detail: `dict | None` requires `from __future__ import annotations` or Python 3.10. I'll just write it as is, assuming a modern environment.

    *   Wait, if I add `process_next_job`, I am deviating from the *exact* list of methods. But if I don't, I can't fulfill requirement 2 ("Jobs should be processed in FIFO order") and requirement 3 ("until they are processed"). I will include it.

    *   Actually, there is a way to do it where the user calls `get_result` and if it's not there, they know it's not processed. But that doesn't handle the "processed in FIFO" part. The "processed" part must happen somewhere. I'll include `process_next_job`.
</reasoning_content>

<content>
```python
from collections import deque

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

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

    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)

    def process_next_job(self):
        """
        Processes the next job in the queue using FIFO order.
        This method simulates the actual processing logic.
        """
        if not self._queue:
            return

        job_id = self._queue.popleft()
        data = self._pending_jobs.pop(job_id)

        # Simulate processing by returning a success dictionary
        self._results[job_id] = {
            "status": "success",
            "processed_data": data
        }
```
</content>