from typing import Protocol, List, Optional
from datetime import datetime, timedelta
from stent.core import ExecutionRecord, TaskRecord, ExecutionProgress, SignalRecord, DeadLetterRecord

class Backend(Protocol):
    async def init_db(self) -> None: ...
    async def close(self) -> None: ...
    # executions
    async def create_execution(self, record: ExecutionRecord) -> None: ...
    async def create_execution_with_root_task(self, record: ExecutionRecord, task: TaskRecord) -> None: ...
    async def get_execution(self, execution_id: str) -> ExecutionRecord | None: ...
    async def update_execution(self, record: ExecutionRecord) -> None: ...
    async def list_executions(self, limit: int = 10, offset: int = 0, state: str | None = None) -> List[ExecutionRecord]: ...
    async def count_executions(self, state: str | None = None) -> int: ...

    # tasks
    async def create_task(self, task: TaskRecord) -> None: ...
    async def create_tasks(self, tasks: List[TaskRecord]) -> None: ...
    async def get_task(self, task_id: str) -> TaskRecord | None: ...
    async def update_task(self, task: TaskRecord) -> None: ...
    async def list_tasks(self, limit: int = 10, offset: int = 0, state: str | None = None) -> List[TaskRecord]: ...
    async def count_tasks(self, queue: str | None = None, state: str | None = None) -> int: ...

    async def claim_next_task(
        self,
        *,
        worker_id: str,
        queues: List[str] | None = None,
        tags: List[str] | None = None,
        now: datetime | None = None,
        lease_duration: timedelta | None = None,
        concurrency_limits: dict[str, int] | None = None,
    ) -> TaskRecord | None: ...

    async def renew_task_lease(
        self,
        task_id: str,
        worker_id: str,
        lease_duration: timedelta,
    ) -> bool: ...

    async def list_tasks_for_execution(self, execution_id: str) -> List[TaskRecord]: ...

    # progress
    async def append_progress(
        self,
        execution_id: str,
        progress: ExecutionProgress,
    ) -> None: ...

    # cache
    async def get_cached_result(self, cache_key: str) -> bytes | None: ...
    async def set_cached_result(self, cache_key: str, value: bytes, ttl: timedelta | None = None) -> None: ...

    async def get_idempotency_result(self, idempotency_key: str) -> bytes | None: ...
    async def set_idempotency_result(self, idempotency_key: str, value: bytes) -> None: ...

    # dead-letter
    async def move_task_to_dead_letter(
        self,
        task: TaskRecord,
        reason: str,
    ) -> None: ...
    async def list_dead_tasks(self, limit: int = 50) -> List[DeadLetterRecord]: ...
    async def count_dead_tasks(self) -> int: ...
    async def get_dead_task(self, task_id: str) -> DeadLetterRecord | None: ...
    async def delete_dead_task(self, task_id: str) -> bool: ...

    async def cleanup_executions(self, older_than: datetime) -> int: ...
    async def cleanup_dead_letters(self, older_than: datetime) -> int: ...

    # signals
    async def create_signal(self, signal: SignalRecord) -> None: ...
    async def get_signal(self, execution_id: str, name: str) -> SignalRecord | None: ...

    # execution context (counters/custom state)
    async def ensure_execution_counters(self, execution_id: str, counters: dict[str, int | float]) -> None: ...
    async def increment_execution_counter(self, execution_id: str, name: str, amount: int | float) -> float: ...
    async def get_execution_counters(self, execution_id: str) -> dict[str, int | float]: ...

    async def ensure_execution_state_values(self, execution_id: str, values: dict[str, bytes]) -> None: ...
    async def set_execution_state_value(self, execution_id: str, key: str, value: bytes) -> None: ...
    async def get_execution_state_values(self, execution_id: str) -> dict[str, bytes]: ...
