from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Mapping

INGRESS_MANUAL_PUSH = "manual_push"
INGRESS_TOPIC = "topic"
INGRESS_DATASET = "dataset"

EGRESS_DEFAULT = "default"
EGRESS_BUFFERED = "buffered"
EGRESS_TOPIC = "topic"
EGRESS_RETURN_ONCE = "return_once"
EGRESS_EXTERNAL_SINK = "external_sink"


@dataclass(frozen=True)
class IngressConnector:
    kind: str
    writable: bool = False

    def start(self, _run) -> None:
        return None

    def stop(self, _run) -> None:
        return None


@dataclass(frozen=True)
class EgressConnector:
    kind: str
    capabilities: frozenset[str] = field(default_factory=frozenset)

    def start(self, _run) -> None:
        return None

    def stop(self, _run) -> None:
        return None


_INGRESS_BY_KIND: dict[str, IngressConnector] = {
    INGRESS_MANUAL_PUSH: IngressConnector(kind=INGRESS_MANUAL_PUSH, writable=True),
    INGRESS_TOPIC: IngressConnector(kind=INGRESS_TOPIC, writable=False),
    INGRESS_DATASET: IngressConnector(kind=INGRESS_DATASET, writable=False),
}

_EGRESS_BY_KIND: dict[str, EgressConnector] = {
    EGRESS_DEFAULT: EgressConnector(kind=EGRESS_DEFAULT, capabilities=frozenset()),
    EGRESS_BUFFERED: EgressConnector(
        kind=EGRESS_BUFFERED,
        capabilities=frozenset({"read", "subscribe"}),
    ),
    EGRESS_TOPIC: EgressConnector(kind=EGRESS_TOPIC, capabilities=frozenset()),
    EGRESS_RETURN_ONCE: EgressConnector(
        kind=EGRESS_RETURN_ONCE,
        capabilities=frozenset({"read", "subscribe"}),
    ),
    EGRESS_EXTERNAL_SINK: EgressConnector(kind=EGRESS_EXTERNAL_SINK, capabilities=frozenset()),
}


def _from_mapping(mapping: Mapping[str, Any], default_kind: str) -> tuple[str, Mapping[str, Any]]:
    kind = mapping.get("kind")
    if kind is None:
        kind = default_kind
    if not isinstance(kind, str) or not kind:
        raise ValueError("Connector kind must be a non-empty string.")
    return kind, mapping


def resolve_ingress_connector(connector: IngressConnector | Mapping[str, Any] | str | None) -> IngressConnector:
    if connector is None:
        return _INGRESS_BY_KIND[INGRESS_MANUAL_PUSH]
    if isinstance(connector, IngressConnector):
        return connector
    if isinstance(connector, str):
        return _INGRESS_BY_KIND.get(connector, IngressConnector(kind=connector, writable=False))
    if isinstance(connector, Mapping):
        kind, mapping = _from_mapping(connector, INGRESS_MANUAL_PUSH)
        default = _INGRESS_BY_KIND.get(kind, IngressConnector(kind=kind, writable=False))
        writable = bool(mapping.get("writable", default.writable))
        return IngressConnector(kind=kind, writable=writable)
    raise TypeError(f"Unsupported ingress connector type: {type(connector)}")


def resolve_egress_connector(connector: EgressConnector | Mapping[str, Any] | str | None) -> EgressConnector:
    if connector is None:
        return _EGRESS_BY_KIND[EGRESS_DEFAULT]
    if isinstance(connector, EgressConnector):
        return connector
    if isinstance(connector, str):
        return _EGRESS_BY_KIND.get(connector, EgressConnector(kind=connector, capabilities=frozenset()))
    if isinstance(connector, Mapping):
        kind, mapping = _from_mapping(connector, EGRESS_BUFFERED)
        default = _EGRESS_BY_KIND.get(kind, EgressConnector(kind=kind, capabilities=frozenset()))
        capabilities = mapping.get("capabilities", default.capabilities)
        if capabilities is None:
            capabilities_set: frozenset[str] = frozenset()
        else:
            capabilities_set = frozenset(str(item) for item in capabilities)
        return EgressConnector(kind=kind, capabilities=capabilities_set)
    raise TypeError(f"Unsupported egress connector type: {type(connector)}")


__all__ = [
    "INGRESS_MANUAL_PUSH",
    "INGRESS_TOPIC",
    "INGRESS_DATASET",
    "EGRESS_DEFAULT",
    "EGRESS_BUFFERED",
    "EGRESS_TOPIC",
    "EGRESS_RETURN_ONCE",
    "EGRESS_EXTERNAL_SINK",
    "IngressConnector",
    "EgressConnector",
    "resolve_ingress_connector",
    "resolve_egress_connector",
]
