Here is the refactored package structure. The code is designed to be modular, type-safe, and free of circular dependencies.

### Directory Structure

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

### 1. `queue/core.py`
Contains the `Job` dataclass and the standard FIFO `JobQueue`. This module has no dependencies on the other modules within the package.

```python
import uuid
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Optional
from collections import deque


@dataclass
class Job:
    """Represents a unit of work to be processed."""
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    payload: Any = None
    priority: int = 0  # Higher number = higher priority
    created_at: datetime = field(default_factory=datetime.now)
    retry_count: int = 0
    max_retries: int = 3
    status: str = "pending"

    def __post_init__(self):
        if self.status not in ["pending", "completed", "failed"]:
            raise ValueError("Status must be pending, completed, or failed")


class JobQueue:
    """A standard FIFO queue for jobs."""

    def __init__(self):
        self._queue: deque[Job] = deque()

    def enqueue(self, job: Job) -> None:
        """Add a job to the end of the queue."""
        self._queue.append(job)

    def dequeue(self) -> Optional[Job]:
        """Remove and return the oldest job."""
        if not self._queue:
            return None
        return self._queue.popleft()

    def peek(self) -> Optional[Job]:
        """Return the oldest job without removing it."""
        if not self._queue:
            return None
        return self._queue[0]

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

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

### 2. `queue/priority.py`
Contains the `PriorityQueue` implementation using a heap. It imports `Job` from `core.py`.

```python
import heapq
from typing import Optional
from core import Job  # Importing from sibling module


class PriorityQueue:
    """A priority queue where higher priority values are processed first."""

    def __init__(self):
        self._heap: list[tuple[int, float, Job]] = []
        self._counter = 0  # Tie-breaker for stability

    def enqueue(self, job: Job) -> None:
        """Add a job to the priority queue."""
        # Invert priority so higher number = lower heap index
        # (heapq is a min-heap, so we negate priority or use tuple ordering)
        # We use (-priority, counter, job) to ensure high priority comes first
        entry = (-job.priority, self._counter, job)
        heapq.heappush(self._heap, entry)
        self._counter += 1

    def dequeue(self) -> Optional[Job]:
        """Remove and return the highest priority job."""
        if not self._heap:
            return None
        _, _, job = heapq.heappop(self._heap)
        return job

    def peek(self) -> Optional[Job]:
        """Return the highest priority job without removing it."""
        if not self._heap:
            return None
        return self._heap[0][2]

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

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

### 3. `queue/retry.py`
Contains the retry logic and exponential backoff. It imports `Job` from `core.py`.

```python
import time
from typing import Callable, Any
from core import Job
from queue.core import JobQueue  # Optional: if retry logic needs to push back to queue


def calculate_backoff(attempt: int, base: float = 1.0, multiplier: float = 2.0) -> float:
    """Calculate exponential backoff delay."""
    return base * (multiplier ** attempt)


def process_with_retry(
    job: Job,
    handler: Callable[[Any], Any],
    max_retries: int | None = None,
    backoff_base: float = 1.0,
    backoff_multiplier: float = 2.0
) -> bool:
    """
    Execute a job handler with exponential backoff retry logic.
    
    Args:
        job: The job object to process (modified in place).
        handler: The function to execute with the job payload.
        max_retries: Override the job's max_retries if provided.
        backoff_base: Base delay in seconds.
        backoff_multiplier: Multiplier for each retry.
        
    Returns:
        True if successful, False if max retries exceeded.
    """
    retries = max_retries if max_retries is not None else job.max_retries
    
    for attempt in range(retries + 1):
        try:
            result = handler(job.payload)
            job.status = "completed"
            return True
        except Exception as e:
            job.retry_count += 1
            
            if attempt == retries:
                job.status = "failed"
                # Log the error or handle failure here
                return False
            
            # Wait before retrying
            delay = calculate_backoff(attempt, backoff_base, backoff_multiplier)
            time.sleep(delay)
            
    return False
```

### 4. `queue/__init__.py`
The public API. It consolidates imports and hides internal implementation details.

```python
from .core import Job, JobQueue
from .priority import PriorityQueue
from .retry import process_with_retry, calculate_backoff

# Define what is exposed when users run: from queue import *
__all__ = [
    "Job",
    "JobQueue",
    "PriorityQueue",
    "process_with_retry",
    "calculate_backoff"
]
```

---

### Usage Example

Here is how a user would interact with the new package structure:

```python
# app.py
from queue import Job, JobQueue, PriorityQueue, process_with_retry

def handler(payload):
    if payload == "fail":
        raise ValueError("Simulated failure")
    return f"Processed: {payload}"

# Using FIFO Queue
fifo = JobQueue()
fifo.enqueue(Job(payload="task_1"))
job = fifo.dequeue()
process_with_retry(job, handler)

# Using Priority Queue
pq = PriorityQueue()
pq.enqueue(Job(payload="urgent", priority=10))
pq.enqueue(Job(payload="normal", priority=1))
job = pq.dequeue()
process_with_retry(job, handler)
```

### Key Refactoring Decisions

1.  **Single Responsibility:**
    *   `core.py` handles data definition and basic FIFO logic.
    *   `priority.py` handles heap logic specifically.
    *   `retry.py` handles error handling and timing logic.
2.  **Dependency Management:**
    *   `core.py` is the foundation and imports nothing from the package itself.
    *   `priority.py` and `retry.py` only import `Job` from `core.py`. This prevents circular imports.
3.  **API Cleanliness:**
    *   `__init__.py` acts as the facade, allowing users to import `Job` and `JobQueue` directly from `queue` without needing to know the internal file structure.
4.  **Type Safety:**
    *   Added type hints (`Optional[Job]`, `Callable`, etc.) to improve IDE support and code clarity.
5.  **Tie-Breaking:**
    *   The `PriorityQueue` uses a sequence counter (`_counter`) to ensure that jobs with the same priority are processed in the order they were added (FIFO stability).