from __future__ import annotations

from datetime import datetime
from typing import TYPE_CHECKING, Iterable, Literal, TypedDict, overload

from cloudevents.abstract import AnyCloudEvent
from cloudevents.conversion import from_dict
from cloudevents.pydantic.v2 import CloudEvent as PydanticCloudEvent
from typing_extensions import Required, Unpack

if TYPE_CHECKING:
    from _typeshed import SupportsKeysAndGetItem


class CloudEventCreateAttributes(TypedDict, total=False):
    """
    Attributes required to create CloudEvent with from_dict().

    Attributes are `Required` if they have no default value. For example, `id`
    is required in CloudEvent objects, but it defaults to a generated UUID.

    See: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#context-attributes
    """

    id: str | None
    source: Required[str]
    specversion: Literal["1.0"]
    type: Required[str]
    datacontenttype: str
    dataschema: str
    subject: str
    time: int | str | datetime | None
    data: object


@overload
def make_cloud_event(
    event_type: type[PydanticCloudEvent] = ...,
    extra_attributes: SupportsKeysAndGetItem[str, object]
    | Iterable[tuple[str, object]]
    | None = ...,
    **attributes: Unpack[CloudEventCreateAttributes],
) -> PydanticCloudEvent:
    ...


@overload
def make_cloud_event(
    event_type: type[AnyCloudEvent],
    extra_attributes: SupportsKeysAndGetItem[str, object]
    | Iterable[tuple[str, object]]
    | None = ...,
    **attributes: Unpack[CloudEventCreateAttributes],
) -> AnyCloudEvent:
    ...


def make_cloud_event(
    event_type: type[AnyCloudEvent] | type[PydanticCloudEvent] = PydanticCloudEvent,
    extra_attributes: SupportsKeysAndGetItem[str, object]
    | Iterable[tuple[str, object]]
    | None = None,
    **attributes: Unpack[CloudEventCreateAttributes],
) -> AnyCloudEvent | PydanticCloudEvent:
    """Create a CloudEvent object with provided attribute values.

    `type` and `source` attributes are required, `id`, `specversion`, `time` and
    `data` have default values.

    See: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#context-attributes

    Examples
    --------
    >>> ce = make_cloud_event(
    ...     id='00000000-0000-0000-0000-000000000000',
    ...     type="com.example.sampletype1",
    ...     source="https://example.com/event-producer",
    ...     time=0,
    ...     data="Hi",
    ... )
    >>> type(ce)
    <class 'cloudevents.pydantic.v2.event.CloudEvent'>

    >>> from cloudevents.conversion import to_json
    >>> print(to_json(ce).decode())
    {\"specversion": "1.0", \
"id": "00000000-0000-0000-0000-000000000000", \
"source": "https://example.com/event-producer", \
"type": "com.example.sampletype1", \
"time": "1970-01-01T00:00:00+00:00", \
"data": "Hi"}
    """

    return from_dict(
        event_type=event_type,
        event={**(dict(extra_attributes) if extra_attributes is not None else {}), **attributes},
    )
