Here is the refactored job queue implementation organized into a clean and modular package structure, maintaining all functionality while separating responsibilities across modules.

---

### `queue/`

```
queue/
├── __init__.py
├── core.py
├── priority.py
└── retry.py
```

---

### `queue/__init__.py`

```python
"""
Job Queue Package

A modular job queue system with priority support and retry logic.
"""

from .core import Job, JobQueue
from .priority import PriorityQueue
from .retry import process_with_retry, RetryConfig

__all__ = [
    "Job",
    "JobQueue",
    "PriorityQueue",
    "RetryConfig",
    "process_with_retry",
]
```

---

### `queue/core.py`

```python
"""
Core job queue functionality.

Defines the Job dataclass and basic queue operations.
"""

from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime
from typing import Callable, Any, Optional
import threading
import queue


@dataclass
class Job:
    """Represents a job to be executed."""
    id: str
    task: Callable
    args: tuple = field(default_factory=tuple)
    kwargs: dict = field(default_factory=dict)
    priority: int = 0
    created_at: datetime = field(default_factory=datetime.utcnow)
    status: str = "pending"
    attempts: int = 0
    max_retries: int = 3
    result: Any = None
    error: Optional[Exception] = None

    def __lt__(self, other: "Job") -> bool:
        return self.priority > other.priority

    def to_dict(self) -> dict:
        return {
            "id": self.id,
            "task": self.task.__name__,
            "args": self.args,
            "kwargs": self.kwargs,
            "priority": self.priority,
            "created_at": self.created_at.isoformat(),
            "status": self.status,
            "attempts": self.attempts,
            "max_retries": self.max_retries,
        }


class JobQueue:
    """Basic FIFO job queue."""

    def __init__(self):
        self._queue: queue.Queue = queue.Queue()
        self._lock = threading.Lock()
        self._jobs: dict[str, Job] = {}

    def add(self, job: Job) -> Job:
        with self._lock:
            self._jobs[job.id] = job
        self._queue.put(job)
        return job

    def get(self, timeout: Optional[float] = None) -> Optional[Job]:
        try:
            return self._queue.get(timeout=timeout)
        except queue.Empty:
            return None

    def get_job(self, job_id: str) -> Optional[Job]:
        with self._lock:
            return self._jobs.get(job_id)

    def size(self) -> int:
        return self._queue.qsize()

    def is_empty(self) -> bool:
        return self._queue.empty()
```

---

### `queue/priority.py`

```python
"""
Priority queue implementation.

Uses heapq for efficient priority-based job retrieval.
"""

import heapq
from typing import Optional
from queue.core import Job


class PriorityQueue:
    """Priority job queue using heap."""

    def __init__(self):
        self._heap: list[tuple[int, int, Job]] = []
        self._counter = 0
        self._lock = threading.Lock()

    def push(self, job: Job) -> None:
        entry = (-job.priority, self._counter, job)
        with self._lock:
            heapq.heappush(self._heap, entry)
            self._counter += 1

    def pop(self) -> Optional[Job]:
        with self._lock:
            if not self._heap:
                return None
            _, _, job = heapq.heappop(self._heap)
            return job

    def peek(self) -> Optional[Job]:
        with self._lock:
            if not self._heap:
                return None
            return self._heap[0][2]

    def size(self) -> int:
        return len(self._heap)

    def is_empty(self) -> bool:
        return len(self._heap) == 0

    def __len__(self) -> int:
        return self.size()
```

---

### `queue/retry.py`

```python
"""
Retry logic with exponential backoff.

Provides utilities for retrying failed jobs.
"""

import time
import random
from typing import Optional, Callable, Any
from queue.core import Job


class RetryConfig:
    """Configuration for retry behavior."""

    def __init__(
        self,
        max_retries: int = 3,
        base_delay: float = 1.0,
        max_delay: float = 60.0,
        jitter: bool = True,
    ):
        self.max_retries = max_retries
        self.base_delay = base_delay
        self.max_delay = max_delay
        self.jitter = jitter

    def get_delay(self, attempt: int) -> float:
        delay = min(
            self.base_delay * (2 ** attempt),
            self.max_delay,
        )
        if self.jitter:
            delay *= (0.5 + random.random())
        return delay


def process_with_retry(
    job: Job,
    func: Callable,
    config: Optional[RetryConfig] = None,
) -> tuple[bool, Optional[Exception]]:
    """
    Execute a job with retry logic.

    Returns:
        (success, error)
    """
    if config is None:
        config = RetryConfig()

    last_error: Optional[Exception] = None

    for attempt in range(job.max_retries + 1):
        try:
            result = func(job)
            job.status = "completed"
            job.result = result
            job.attempts = attempt + 1
            return True, None
        except Exception as e:
            last_error = e
            job.error = e
            job.attempts += 1

            if attempt < job.max_retries - 1:
                delay = config.get_delay(attempt)
                time.sleep(delay)

    job.status = "failed"
    return False, last_error
```

---

### Summary of Responsibilities

| Module       | Responsibility                          |
|--------------|------------------------------------------|
| `core.py`    | `Job` dataclass and basic `JobQueue`     |
| `priority.py`| Priority queue with heap-based ordering  |
| `retry.py`   | Retry logic and exponential backoff      |
| `__init__.py`| Clean public API exports                 |

This modular design ensures:
- ✅ Single Responsibility Principle
- ✅ Clear separation of concerns
- ✅ Maintainable and testable modules
- ✅ Clean public API for consumers