from __future__ import annotations

from abc import ABC
from dataclasses import dataclass, field
from datetime import timedelta
from typing import (
    Generic,
    Mapping,
    Optional,
    Sequence,
)

from nexusrpc._common import Link, OutputT


@dataclass(frozen=True)
class OperationContext(ABC):
    """Context for the execution of the requested operation method.

    Includes information from the request."""

    def __new__(cls, *args, **kwargs):
        if cls is OperationContext:
            raise TypeError(
                "OperationContext is an abstract class and cannot be instantiated directly"
            )
        return super().__new__(cls)

    service: str
    """
    The name of the service that the operation belongs to.
    """

    operation: str
    """
    The name of the operation.
    """

    headers: Mapping[str, str]
    """
    Optional header fields sent by the caller.
    """


@dataclass(frozen=True)
class StartOperationContext(OperationContext):
    """Context for the start method.

    Includes information from the request."""

    request_id: str
    """
    Request ID that may be used by the handler to dedupe a start request.
    By default a v4 UUID should be generated by the client.
    """

    callback_url: Optional[str] = None
    """
    A callback URL is required to deliver the completion of an async operation. This URL should be
    called by a handler upon completion if the started operation is async.
    """

    callback_headers: Mapping[str, str] = field(default_factory=dict)
    """
    Optional header fields set by the caller to be attached to the callback request when an
    asynchronous operation completes.
    """

    inbound_links: Sequence[Link] = field(default_factory=list)
    """
    Links received in the request. This list is automatically populated when handling a start
    request. Handlers may use these links, for example to add information about the
    caller to a resource associated with the operation execution.
    """

    outbound_links: list[Link] = field(default_factory=list)
    """
    Links to be returned by the handler. This list is initially empty. Handlers may
    populate this list, for example with links describing resources associated with the
    operation execution that may be of use to the caller.
    """


@dataclass(frozen=True)
class CancelOperationContext(OperationContext):
    """Context for the cancel method.

    Includes information from the request."""


@dataclass(frozen=True)
class FetchOperationInfoContext(OperationContext):
    """Context for the fetch_info method.

    Includes information from the request."""


@dataclass(frozen=True)
class FetchOperationResultContext(OperationContext):
    """Context for the fetch_result method.

    Includes information from the request."""

    wait: Optional[timedelta] = None
    """
    Allowed time to wait for the operation result (long poll). If by the end of the
    wait period the operation is still running, a response with 412 status code will
    be returned, and the caller may re-issue the request to start a new long poll.
    """


@dataclass(frozen=True)
class StartOperationResultSync(Generic[OutputT]):
    """
    A result returned synchronously by the start method of a nexus operation handler.
    """

    value: OutputT  # type: ignore[misc]
    """
    The value returned by the operation.
    """


@dataclass(frozen=True)
class StartOperationResultAsync:
    """
    A value returned by the start method of a nexus operation handler indicating that
    the operation is responding asynchronously.
    """

    token: str
    """
    A token representing the in-progress operation that the caller can submit with
    subsequent ``fetch_info``, ``fetch_result``, or ``cancel`` requests.
    """
