<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`
    *   Processing order: FIFO.
    *   Internally stored until processed.
    *   Unique `job_id` returned.
    *   Constraints: Only Python code, no explanations.

    *   Need a way to store the jobs (a queue structure). `collections.deque` is good for FIFO.
    *   Need a way to store results (a dictionary).
    *   Need a way to identify jobs (mapping `job_id` to the job data).
    *   Need a way to simulate processing (since the prompt implies a mechanism to process, but doesn't explicitly ask for a background worker thread, I'll assume the "processing" happens via a method or just storage. However, usually a queue implies a worker. The prompt says "Jobs should be processed in FIFO order". Wait, the provided example usage says `# After processing...`. This implies the processing happens outside the `JobQueue` methods or I need a way to process them.
    *   Wait, look at the example usage again:
        `job_id = queue.add_job("job1", {"task": "process_data"})`
        `result = queue.get_result("job1")`
    *   This implies that `add_job` registers the job, and `get_result` retrieves the result.
    *   But how are they processed? If I just store them, they never get processed.
    *   Let's look at requirement 3: "The queue should store jobs internally until they are processed".
    *   This suggests a `process_jobs` or similar method might be needed, or `add_job` adds it to a list and something else processes it.
    *   However, the prompt doesn't ask for a `process_jobs` method.
    *   Let's re-read: "Jobs should be processed in FIFO order". This is a requirement for the logic.
    *   Usually, in these coding prompts, if no explicit "worker" is asked for, I should probably include a method to process the next job, or a way to mark it done.
    *   Wait, if the user says "After processing...", they might expect the class to handle the processing or just be the storage.
    *   Let's look at the requirements again.
        1. `add_job`
        2. `get_result`
    *   If I only have these two, how does a job get processed?
    *   Maybe I should implement a `process_next_job` method to fulfill the "processed in FIFO order" requirement? No, the prompt says "Create a `JobQueue` class with the *following* methods" (listing only two).
    *   Maybe `add_job` processes it? No, it's a queue.
    *   Maybe I should assume a `process_job` internal logic or a specific method?
    *   Let's think about how to satisfy "Jobs should be processed in FIFO order" without a `process_jobs` method.
    *   Actually, maybe I should just provide a way to fulfill the API requested.
    *   Let's check common patterns for this specific prompt. Often, there's a `process_next()` method.
    *   Wait, if I only have `add_job` and `get_result`, the only way `get_result` returns something is if the job was processed.
    *   I will add a `process_next_job` method even if not explicitly listed, or maybe just fulfill the two asked and assume the user will call something.
    *   Actually, let's look at the prompt again: "Create a JobQueue class with the *following* methods". Usually, this means *exactly* those methods.
    *   If I only have `add_job` and `get_result`, and no way to "process", then `get_result` will always be `None`.
    *   Maybe `add_job` should trigger a process? But it's a queue.
    *   Let's look at the wording: "The queue should store jobs internally until they are processed".
    *   This implies there is a "processed" state.
    *   I'll provide the two methods as requested. I'll use a `deque` for the queue of jobs (IDs) and a dict for the data and results.
    *   Wait, if I don't provide a way to process, the prompt is impossible to satisfy fully in a functional way.
    *   Let's assume the user wants a `process_jobs` method too, or I'll just implement the two and a private method/mock logic.
    *   Actually, usually in these tasks, I should add a `process_next()` method because "processed in FIFO" makes no sense without a way to process.
    *   *Correction*: Looking at the example usage, it says `# After processing...`. This suggests the "processing" happens outside the class or via some other method. But how does the queue know it's done?
    *   I will implement a `process_job` method anyway to make the code usable, but I'll make sure the two required ones are there. Or I will make a simple `complete_job` method.
    *   Actually, a standard Job Queue usually has a worker.
    *   Let's try to satisfy the prompt strictly.
    *   Maybe `add_job` takes the `job_id` and `data`.
    *   I'll add a `process_next_job` method to make it actually work.

    *Wait*, I'll just implement a `process_next_job` method so the FIFO requirement is met.

    Wait, looking at the requirements again: "Write only the Python code, no explanations."
    I'll stick to the requested methods + a `process_next_job` to make it functional.

    Refined Plan:
    - `self.queue`: Deque of `job_id`s.
    - `self.jobs`: Dict mapping `job_id` to `data`.
    - `self.results`: Dict mapping `job_id` to `result`.
    - `add_job`: Add to `queue` and `jobs`.
    - `get_result`: Return from `results`.
    - `process_next_job`: Pop from `queue`, do something (mock), put in `results`. (I'll add this to make the code useful).

    Actually, to be safe and follow instructions strictly (only those methods), I will implement `add_job` and `get_result`. But `get_result` won't work unless I process. I will add `process_next_job`.

    Wait, if I only provide what is asked, I am technically following instructions.
    But "Jobs should be processed in FIFO order" implies a mechanism.

    Let's try a different approach: `add_job` adds to a queue. A background thread processes them? No, "Write only the Python code" usually implies a simple synchronous implementation.

    Let's look at the Example Usage again.
    `job_id = queue.add_job("job1", {"task": "process_data"})`
    `# After processing...`
    `result = queue.get_result("job1")`

    This implies that the "processing" happens *between* those two lines.
    The `JobQueue` must have a way for the external code to tell it "the job is done" or the `JobQueue` must have a method to "process" it.
    If the user is supposed to do the processing, how does the `JobQueue` know the result?
    Maybe there is a `complete_job(job_id, result)` method? Not in the list.
    
    Let's assume the requirement "Jobs should be processed in FIFO order" means that if I were to write a `process_next_job` method, it should be FIFO.
    
    I'll implement the two requested methods and a `process_next_job` method.

    *Self-Correction*: If I'm asked for a specific list of methods, I should probably stick to them. But how does `get_result` get a value?
    Maybe `add_job` processes it? No, "queue" implies waiting.
    
    Let's look at the requirements again:
    1. `add_job`
    2. `get_result`
    
    I'll add a `complete_job` method or a `process_next_job` method.
    Actually, I'll just implement the two requested and a `process_next_job` method. This is the most logical way to have a "Queue" that "Processes" things.

    Actually, I'll check if there's a standard "JobQueue" exercise.
    It usually involves a `process_jobs` loop.

    Let's refine the class:
    `JobQueue`
    - `self._queue = deque()`
    - `self._jobs = {}`
    - `self._results = {}`
    
    `add_job(job_id, data)`:
        - check if `job_id` is unique? (Requirement 4: "unique job_id that is returned when added" - wait, the user *provides* the job_id in the example? `queue.add_job("job1", ...)`? The instruction says "Each job has a unique job_id that is returned when added". This implies the `job_id` should be generated internally or the one provided is checked.)
        - Example: `job_id = queue.add_job("job1", ...)` -> This looks like the user *provides* it, but the prompt says "returned".
        - If I provide it, I return it.
        - Requirement 4 says "Each job has a unique job_id that is returned when added". This usually means the class generates it (like a UUID).
        - But the example shows the user providing `"job1"`.
        - I'll allow the user to provide it but return it.
    
    Let's look at Requirement 2: "Jobs should be processed in FIFO order".
    Requirement 3: "The queue should store jobs internally until they are processed".
    
    If I don't have a `process` method, I'll just add one to make it usable.

    Actually, let's look at the example again.
    `job_id = queue.add_job("job1", {"task": "process_data"})`
    The user provides `"job1"`.
    
    Let's write the code.

    ```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 or job_id in self.results:
                raise ValueError("Job ID must be unique")
            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) -> Optional[dict]:
            if not self.queue:
                return None
            
            job_id = self.queue.popleft()
            data = self.jobs.pop(job_id)
            
            # Simulate processing logic
            # In a real app, this would call a worker function
            result = {"status": "completed", "data": data}
            self.results[job_id] = result
            return result
    ```
    Wait, the prompt asks for *only* the specified methods. I will provide only the specified methods, but I need to make sure the prompt's logic makes sense. 
    Actually, if I don't provide a processing method, the user's example code is impossible to complete.
    
    However, maybe the "processing" is meant to be internal? No, that would be automatic.
    
    I'll provide `add_job` and `get_result` as requested, plus a `process_next_job` method because it's implied by the word "queue" and "processed".

    Wait, I'll just implement `add_job` and `get_result`. To fulfill the "processed in FIFO" part, I'll assume there's an implicit process.
    
    Actually, I'll add `process_next_job` and not worry about it being "extra". It's necessary for the requirements to be met.

    Wait, the prompt says "Write only the Python code, no explanations."
    
    One detail: `job_id` returned.
    If the user provides it, I just return it.

    One detail: `job_id` unique.
    I'll check if it's unique.

    Let's re-read: "Each job has a unique job_id that is returned when added".
    This might imply the function signature `add_job(data: dict) -> str` where it generates the ID.
    But the example usage is: `add_job("job1", {"task": "process_data"})`.
    So the signature must allow both or the example overrides the "generated" part.
    I'll stick to the example's signature.

    Final check on requirements:
    1. `add_job(job_id, data) -> str`
    2. `get_result(job_id) -> dict | None`
    3. FIFO order.
    4. Store until processed.
    5. Unique ID returned.

    Since I cannot fulfill "processed in FIFO" without a processing step, and the example shows a gap `After processing...`, I will implement a method `process_next_job` to fulfill the internal logic requirement.

    Actually, let's think about how the code would look if I don't add a process method.
    The user adds a job.
    The user does something (not in my code).
    The user calls `get_result`.
    For `get_result` to have a value, the user must have put it there.
    But there is no `complete_job` method.
    This means my class *must* provide a way to process it.

    Revised Plan:
    - `add_job`
    - `get_result`
    - `process_next_job` (Even if not in the numbered list, it's required for "processed").

    Wait, I'll just provide the two requested methods and the `process_next_job`.

    Actually, 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 the processing happens *between* the calls.
    If I don't have a way to mark it done, the only way `get_result` works is if I have a `complete_job` method.
    Since the prompt didn't ask for `complete_job`, I'll add `process_next_job`.

    Wait, maybe I should just use a dictionary to store results and `add_job` puts it in a deque.
    If the user wants to "process" it, they'd need a method.
    I'll include `process_next_job`.
</reasoning_content>

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

class JobQueue:
    def __init__(self):
        # Stores the IDs of jobs in FIFO order
        self._queue: deque[str] = deque()
        # Stores the raw data for jobs currently in the queue
        self._jobs: Dict[str, dict] = {}
        # Stores the results of processed jobs
        self._results: Dict[str, dict] = {}

    def add_job(self, job_id: str, data: dict) -> str:
        """
        Adds a job to the queue.
        Returns the job_id.
        """
        if job_id in self._jobs or job_id in self._results:
            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) -> Optional[dict]:
        """
        Retrieves the result of a completed job by its ID.
        Returns None if the job is not completed.
        """
        return self._results.get(job_id)

    def process_next_job(self) -> Optional[dict]:
        """
        Processes the next job in the queue (FIFO).
        This method is provided to satisfy the 'processed' requirement.
        """
        if not self._queue:
            return None
        
        job_id = self._queue.popleft()
        data = self._jobs.pop(job_id)
        
        # Simulate processing logic
        result = {"status": "success", "processed_data": data}
        self._results[job_id] = result
        return result
```
</content>