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

class JobQueue:
    def __init__(self):
        self._queue = deque()
        self._jobs: Dict[str, dict] = {}
        self._processed: set = set()
    
    def add_job(self, job_id: str, data: dict) -> str:
        """Add a job to the queue, return job_id"""
        if job_id in self._processed:
            raise ValueError(f"Job {job_id} has already been processed")
        
        self._queue.append(job_id)
        self._jobs[job_id] = {
            'data': data,
            'status': 'pending',
            'result': None
        }
        return job_id
    
    def get_result(self, job_id: str) -> Optional[dict]:
        """Get the result of a completed job"""
        if job_id not in self._jobs:
            return None
        
        job = self._jobs[job_id]
        if job['status'] == 'completed':
            return job['result']
        return None
    
    def process_next_job(self) -> Optional[dict]:
        """Process the next job in the queue (FIFO order)"""
        if not self._queue:
            return None
        
        job_id = self._queue.popleft()
        if job_id not in self._jobs:
            return None
        
        job = self._jobs[job_id]
        if job['status'] != 'pending':
            return None
        
        # Simulate job processing
        job['status'] = 'completed'
        job['result'] = {
            'job_id': job_id,
            'data': job['data'],
            'status': 'completed'
        }
        self._processed.add(job_id)
        return job['result']
    
    def process_all_jobs(self) -> list:
        """Process all jobs in the queue and return results"""
        results = []
        while self._queue:
            result = self.process_next_job()
            if result:
                results.append(result)
        return results
```